Прогнозирование восстановления золота из руды¶

Компания «Цифра» разрабатывает решения для эффективной работы промышленных предприятий. Ее клиенту, предприятию, занимающемуся добычей и очисткой золотоносной руды, необходимо оптимизировать производство, чтобы не запускать процессы с убыточными характеристиками.

Задача: Спрогнозировать коэффициент восстановления золота из руды с помощью модели машинного обучения.
Качество модели необходимо оценить по метрике sMAPE, симметричному среднему абсолютному процентному отклонению (Symmetric Mean Absolute Percentage Error).

Описание получения золота из руды

Добытая руда проходит первичную обработку, затем её отправляют на флотацию (обогащение) и двухэтапную очистку.

  1. Флотация Во флотационную установку подаётся смесь золотосодержащей руды. После обогащения получается черновой концентрат и «отвальные хвосты», то есть остатки продукта с низкой концентрацией ценных металлов. На стабильность этого процесса влияет непостоянное и неоптимальное физико-химическое состояние флотационной пульпы (смеси твёрдых частиц и жидкости).
  2. Очистка Черновой концентрат проходит две очистки. На выходе получается финальный концентрат и новые отвальные хвосты.

Этапы обработки

  • rougher — флотация
  • primary_cleaner — первичная очистка
  • secondary_cleaner — вторичная очистка
  • final — финальные характеристики

Параметры этапов

  • air amount — объём воздуха
  • fluid levels — уровень жидкости
  • feed size — размер гранул сырья
  • feed rate — скорость подачи

Возможные значения для блока [тип_параметра]:

  • input — параметры сырья
  • output — параметры продукта
  • state — параметры, характеризующие текущее состояние этапа
  • calculation — расчётные характеристики

Описание данных

Данные разделены на три датасета - исходный, обучающий и тестовый.

Имеется информация об исходном сырье и с данными после его очистки. Данные индексируются датой и временем получения информации (признак date). Показатели соседних партий обычно схожи.

  • Rougher feed — исходное сырье
  • Rougher additions (или reagent additions) — флотационные реагенты: Xanthate, Sulphate, Depressant
    • Xanthate — ксантогенат (промотер, или активатор флотации);
    • Sulphate — сульфат (на данном производстве сульфид натрия);
    • Depressant — депрессант (силикат натрия).
  • Rougher process (англ. «грубый процесс») — флотация
  • Rougher tails — отвальные хвосты
  • Float banks — флотационная установка
  • Cleaner process — очистка
  • Rougher Au — черновой концентрат золота
  • Final Au — финальный концентрат золота

Наименование признаков
[этап].[тип_параметра].[название_параметра]
Пример: rougher.input.feed_ag

Особенности:
Некоторые параметры недоступны, потому что замеряются и/или рассчитываются значительно позже. Из-за этого в тестовой выборке отсутствуют некоторые признаки, которые могут быть в обучающей. Также в тестовом наборе нет целевых признаков. Исходный датасет содержит обучающую и тестовую выборки со всеми признаками.

Содержание

  • Обзор и подготовка данных
    • Проверка рассчетов
  • Исследовательский анализ данных
    • Анализ признаков и их взаимодействий
      • Целевые признаки и доли золота в разных субстратах
      • Размер гранул сырья и скорость их подачи
      • Флотационные реагенты
      • Физические параметры
      • Доля металлов в руде
    • Изменение концентрации металлов на различных этапах
    • Обработка аномальных значений
    • Обработка пропущенных значений
  • Исследование моделей
    • Прогноз коэффициента обогащения чернового концентрата
      • Линейная регрессия
      • Случайный лес
      • Cлучайная модель
    • Прогноз коэффициента обогащения финального продукта
      • Линейная регрессия
      • Случайный лес
      • Cлучайная модель
  • Тестирование моделей
    • Предсказание коэффициента обогащения для чернового концентрата
    • Предсказание коэффициента обогащения для финального продукта
    • Итоговая метрика
  • Общий вывод:
In [1]:
#!pip install phik
In [2]:
# import libraries 
import datetime
import os
import warnings
warnings.filterwarnings('ignore')

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns

from phik.report import correlation_report, plot_correlation_matrix

from sklearn.dummy import DummyRegressor
from sklearn.ensemble import RandomForestRegressor
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, make_scorer
from sklearn.model_selection import RandomizedSearchCV
from sklearn.model_selection import cross_val_score
In [3]:
# display settings
pd.options.display.max_columns = None
pd.set_option('display.max_colwidth', 100)
pd.set_option("display.precision", 4)

%matplotlib inline

Обзор и подготовка данных¶

Из описания данных известно, что признак date - дата и время получения информации, при чтении файла приведем его сразу в формат datetime.

In [4]:
# data path
path = os.path.join(os.path.dirname(os.getcwd()), '9_Project2')
In [5]:
# define function to read files and display information about the data
def read_file(dataset_type):
    """ Функция принимает строку - 
    тип датасета (full, train, tast).
    Читает данные, выводит основную информацию
    по файлу, возвращает датафрейм с данными"""
    
    # reading files
    filename = 'gold_recovery_' + dataset_type + '_new.csv'
    try:
        df = pd.read_csv(os.path.join(path, filename), 
                         parse_dates=['date'])
    except:
        df = pd.read_csv('/datasets/'+filename, 
                         parse_dates=['date'])
        
    # get first and last 5 rows
    display(df)
    
    # get information about the data
    print()
    print(df.info())
    
    # check for duplicates
    print()
    if df.duplicated().sum() > 0:
        print('В данных есть явные дубликаты')
    else:
        print('В данных нет явных дубликатов')
    
    # check for NaN
    if df.isna().sum().sum() > 0:
        print('В данных есть пропущенные значения')
    else:
        print('В данных нет пропущенных значений')
    
    return df
In [6]:
# complete data
data_full = read_file('full')
date final.output.concentrate_ag final.output.concentrate_pb final.output.concentrate_sol final.output.concentrate_au final.output.recovery final.output.tail_ag final.output.tail_pb final.output.tail_sol final.output.tail_au primary_cleaner.input.sulfate primary_cleaner.input.depressant primary_cleaner.input.feed_size primary_cleaner.input.xanthate primary_cleaner.output.concentrate_ag primary_cleaner.output.concentrate_pb primary_cleaner.output.concentrate_sol primary_cleaner.output.concentrate_au primary_cleaner.output.tail_ag primary_cleaner.output.tail_pb primary_cleaner.output.tail_sol primary_cleaner.output.tail_au primary_cleaner.state.floatbank8_a_air primary_cleaner.state.floatbank8_a_level primary_cleaner.state.floatbank8_b_air primary_cleaner.state.floatbank8_b_level primary_cleaner.state.floatbank8_c_air primary_cleaner.state.floatbank8_c_level primary_cleaner.state.floatbank8_d_air primary_cleaner.state.floatbank8_d_level rougher.calculation.sulfate_to_au_concentrate rougher.calculation.floatbank10_sulfate_to_au_feed rougher.calculation.floatbank11_sulfate_to_au_feed rougher.calculation.au_pb_ratio rougher.input.feed_ag rougher.input.feed_pb rougher.input.feed_rate rougher.input.feed_size rougher.input.feed_sol rougher.input.feed_au rougher.input.floatbank10_sulfate rougher.input.floatbank10_xanthate rougher.input.floatbank11_sulfate rougher.input.floatbank11_xanthate rougher.output.concentrate_ag rougher.output.concentrate_pb rougher.output.concentrate_sol rougher.output.concentrate_au rougher.output.recovery rougher.output.tail_ag rougher.output.tail_pb rougher.output.tail_sol rougher.output.tail_au rougher.state.floatbank10_a_air rougher.state.floatbank10_a_level rougher.state.floatbank10_b_air rougher.state.floatbank10_b_level rougher.state.floatbank10_c_air rougher.state.floatbank10_c_level rougher.state.floatbank10_d_air rougher.state.floatbank10_d_level rougher.state.floatbank10_e_air rougher.state.floatbank10_e_level rougher.state.floatbank10_f_air rougher.state.floatbank10_f_level secondary_cleaner.output.tail_ag secondary_cleaner.output.tail_pb secondary_cleaner.output.tail_sol secondary_cleaner.output.tail_au secondary_cleaner.state.floatbank2_a_air secondary_cleaner.state.floatbank2_a_level secondary_cleaner.state.floatbank2_b_air secondary_cleaner.state.floatbank2_b_level secondary_cleaner.state.floatbank3_a_air secondary_cleaner.state.floatbank3_a_level secondary_cleaner.state.floatbank3_b_air secondary_cleaner.state.floatbank3_b_level secondary_cleaner.state.floatbank4_a_air secondary_cleaner.state.floatbank4_a_level secondary_cleaner.state.floatbank4_b_air secondary_cleaner.state.floatbank4_b_level secondary_cleaner.state.floatbank5_a_air secondary_cleaner.state.floatbank5_a_level secondary_cleaner.state.floatbank5_b_air secondary_cleaner.state.floatbank5_b_level secondary_cleaner.state.floatbank6_a_air secondary_cleaner.state.floatbank6_a_level
0 2016-01-15 00:00:00 6.0554 9.8896 5.5073 42.1920 70.5412 10.4120 0.8954 16.9043 2.1431 127.0920 10.1283 7.25 0.9888 8.5476 10.3896 19.5293 34.1744 14.9365 2.5349 7.4761 2.1067 1549.7758 -498.9121 1551.4342 -516.4034 1549.8739 -498.6666 1554.3674 -493.4281 41885.7070 3481.7791 3520.3372 2.8387 6.1004 2.2849 523.5463 55.4866 36.8086 6.4861 11.9866 6.0080 11.8367 6.0058 11.5008 7.1011 28.0293 19.7938 87.1078 5.0080 0.5087 19.1543 1.1702 999.7069 -404.0670 1603.0114 -434.7150 1602.3750 -442.2045 1598.9373 -451.2941 1404.4720 -455.4630 1416.3550 -451.9396 14.5002 4.6948 8.7646 2.6062 25.8531 -498.5265 23.8937 -501.4063 23.9618 -495.2628 21.9404 -499.3410 14.0168 -502.4880 12.0999 -504.7159 9.9256 -498.3102 8.0797 -500.4710 14.1513 -605.8420
1 2016-01-15 01:00:00 6.0294 9.9689 5.2578 42.7016 69.2662 10.4627 0.9275 16.6345 2.2249 125.6292 10.2963 7.25 1.0027 8.5587 10.4971 19.3691 34.1185 16.2505 3.0496 6.7339 2.3530 1576.1667 -500.9050 1575.9506 -499.8659 1575.9942 -499.3151 1574.4793 -498.9317 42050.8618 3498.3710 3489.9819 2.8590 6.1611 2.2660 525.2906 57.2787 35.7534 6.4786 11.9712 6.0058 11.9962 6.0126 11.6159 7.2788 28.0671 20.0510 86.8433 4.9554 0.5367 18.9652 1.1848 1000.2864 -400.0652 1600.7546 -449.9534 1600.4796 -449.8306 1600.5276 -449.9536 1399.2271 -450.8698 1399.7195 -450.1190 14.2655 4.5925 9.0015 2.4882 25.8805 -499.9897 23.8895 -500.3724 23.9706 -500.0855 22.0857 -499.4469 13.9923 -505.5033 11.9505 -501.3315 10.0392 -500.1700 7.9848 -500.5822 13.9984 -599.7872
2 2016-01-15 02:00:00 6.0559 10.2140 5.3838 42.6575 68.1164 10.5070 0.9537 16.2088 2.2579 123.8198 11.3163 7.25 0.9913 8.6035 10.3545 19.1676 33.9695 16.4918 3.1247 6.4718 2.4168 1601.5562 -499.9978 1600.3867 -500.6078 1602.0035 -500.8701 1599.5415 -499.8274 42018.1012 3495.3489 3502.3598 2.9460 6.1165 2.1596 530.0266 57.5106 35.9716 6.3622 11.9206 6.1974 11.9203 6.2046 11.6958 7.2168 27.4540 19.7372 86.8423 4.8435 0.5464 18.8085 1.1626 999.7196 -400.0740 1599.3373 -450.0085 1599.6728 -449.9545 1599.8493 -449.9542 1399.1809 -449.9376 1400.3167 -450.5271 14.1157 4.6248 8.8429 2.4582 26.0052 -499.9296 23.8867 -499.9519 23.9135 -499.4423 23.9577 -499.9020 14.0150 -502.5209 11.9128 -501.1334 10.0709 -500.1291 8.0139 -500.5176 14.0287 -601.4274
3 2016-01-15 03:00:00 6.0480 9.9770 4.8586 42.6898 68.3475 10.4228 0.8838 16.5328 2.1468 122.2702 11.3221 7.25 0.9967 7.2219 8.4966 15.9785 28.2607 16.0244 2.9604 6.8438 2.2621 1599.9687 -500.9518 1600.6592 -499.6771 1600.3041 -500.7280 1600.4495 -500.0526 42029.4480 3498.5783 3499.1629 3.0023 6.0433 2.0378 542.5904 57.7927 36.8622 6.1182 11.6301 6.2032 11.6924 6.1966 11.9150 7.1756 27.3413 19.3208 87.2264 4.6553 0.5425 19.3302 1.0798 999.8148 -400.2002 1600.0594 -450.6199 1600.0128 -449.9105 1597.7252 -450.1301 1400.9432 -450.0301 1400.2347 -449.7908 13.7321 4.4825 9.1229 2.3221 25.9425 -499.1767 23.9555 -499.8488 23.9668 -500.0088 23.9544 -499.9447 14.0365 -500.8573 11.9996 -501.1937 9.9704 -499.2016 7.9773 -500.2559 14.0056 -599.9961
4 2016-01-15 04:00:00 6.1486 10.1425 4.9394 42.7741 66.9270 10.3603 0.7928 16.5257 2.0553 117.9882 11.9136 7.25 1.0099 9.0894 9.9868 19.1999 33.0449 16.4802 3.1121 6.5502 2.2771 1601.3397 -498.9755 1601.4379 -500.3232 1599.5819 -500.8882 1602.6495 -500.5930 42125.3542 3494.8008 3506.6793 3.1696 6.0609 1.7869 540.5319 56.0472 34.3477 5.6637 10.9578 6.1988 10.9605 6.1949 12.4111 7.2402 27.0410 19.2161 86.6888 4.5528 0.5154 19.2674 1.0126 999.6787 -399.7527 1600.2088 -449.5996 1600.3577 -450.0344 1599.7590 -449.9098 1401.5609 -448.8772 1401.1602 -450.4071 14.0800 4.4707 8.8710 2.3304 26.0248 -500.2791 23.9553 -500.5936 23.9857 -500.0838 23.9589 -499.9903 14.0273 -499.8386 11.9531 -501.0539 9.9257 -501.6867 7.8942 -500.3560 13.9966 -601.4967
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
19434 2018-08-18 06:59:59 3.2249 11.3562 6.8035 46.7140 73.7551 8.7696 3.1415 10.4032 1.5292 123.3818 8.0289 6.50 1.3042 6.5592 12.4460 7.0563 32.9402 22.8546 6.5315 3.9343 2.3144 1648.4212 -400.3822 1648.7420 -400.3597 1648.5782 -399.3636 1648.8340 -399.6692 45912.9426 2497.6530 2499.1780 1.1556 6.0919 4.6176 560.8891 85.7183 37.3698 5.3359 7.7628 9.1586 7.7667 9.1561 11.1249 10.9840 30.0689 18.6036 89.5744 4.2073 0.6129 16.6666 0.7486 1199.2459 -300.8455 1149.8079 -498.7897 1047.9636 -498.4131 946.6410 -499.1525 849.6649 -499.2145 849.7581 -497.4487 0.0000 0.0000 0.0000 0.0000 35.0432 -499.0457 29.9067 -499.9799 26.0024 -499.9534 22.9872 -499.9674 23.0315 -501.1679 20.0076 -499.7400 18.0060 -499.8344 13.0011 -500.1557 20.0078 -501.2964
19435 2018-08-18 07:59:59 3.1960 11.3494 6.8622 46.8668 69.0493 8.8973 3.1305 10.5495 1.6125 120.8782 7.9626 6.50 1.3024 6.6124 12.6239 7.5423 32.9253 23.0025 6.6297 4.0891 2.4629 1649.8202 -399.9310 1649.3575 -399.7212 1648.6562 -401.1958 1649.7251 -400.6363 46200.0996 2614.4039 2518.5501 1.1673 6.1213 4.1450 559.0318 119.4992 38.5916 4.8386 7.3567 9.3050 7.0955 9.2979 11.4260 10.8882 29.7840 18.4414 87.7240 4.1779 0.6506 16.9606 0.7716 1196.5693 -299.5122 1147.6752 -500.6083 1048.5657 -500.9328 949.7736 -500.0231 848.5152 -500.2894 850.0131 -496.8221 0.0000 0.0000 0.0000 0.0000 35.0261 -499.8919 29.9218 -499.9497 26.0317 -500.3846 22.9911 -500.0796 22.9601 -501.6128 20.0357 -500.2514 17.9985 -500.3952 12.9540 -499.8952 19.9685 -501.0416
19436 2018-08-18 08:59:59 3.1100 11.4344 6.8860 46.7957 67.0022 8.5296 2.9114 11.1151 1.5966 105.6661 7.9551 6.50 1.3159 7.1289 12.6336 7.9419 31.8567 22.2981 6.2783 4.4575 2.6122 1649.1668 -399.8886 1649.1969 -399.6776 1647.8970 -399.9883 1649.7727 -399.8319 44585.1813 2510.8135 2510.1424 1.1256 5.9705 4.0200 555.6829 122.2627 40.0740 4.5251 6.5860 9.2996 6.5841 9.3001 8.5235 8.9551 22.7865 15.1112 88.8906 4.0500 0.6361 18.3231 0.6851 1204.8666 -299.2357 1149.9429 -501.7179 1049.6044 -500.5491 952.7027 -502.3523 849.0160 -500.5057 850.4556 -506.8980 0.0000 0.0000 0.0000 0.0000 35.0036 -501.0838 29.9905 -611.8559 25.9484 -500.0673 22.9683 -499.8394 23.0157 -501.7116 19.9512 -499.8570 18.0195 -500.4512 13.0234 -499.9144 19.9909 -501.5185
19437 2018-08-18 09:59:59 3.3672 11.6256 6.7994 46.4082 65.5232 8.7772 2.8192 10.4638 1.6029 98.8805 7.9842 6.50 1.2420 7.4904 12.0124 6.9430 30.7709 22.7147 6.2531 3.5571 2.8006 1646.5478 -398.9771 1648.2122 -400.3833 1648.9174 -399.5213 1651.4986 -399.7453 44084.7357 2507.5725 2507.7234 1.1179 6.0481 3.9025 544.7317 123.7424 39.7139 4.3628 6.2101 9.2977 6.2095 9.2972 11.6588 10.6554 29.0527 17.8348 89.8581 3.9025 0.6256 19.4237 0.5671 1201.9042 -299.9073 1154.0878 -500.0366 1054.0098 -500.2373 944.1388 -496.8670 851.5898 -499.0405 851.3456 -499.1226 0.0000 0.0000 0.0000 0.0000 34.9807 -498.1310 29.9685 -586.0133 25.9717 -499.6084 22.9584 -499.8213 23.0250 -501.1534 20.0541 -500.3147 17.9795 -499.2729 12.9924 -499.9763 20.0140 -500.6255
19438 2018-08-18 10:59:59 3.5984 11.7378 6.7175 46.2994 70.2815 8.4067 2.5175 10.6522 1.3894 95.2484 8.0790 6.50 1.2830 7.5376 11.6503 6.2036 30.3566 22.7359 6.0843 3.0267 2.7080 1648.7599 -399.8621 1650.1354 -399.9573 1648.8319 -400.5861 1649.4646 -400.6733 44082.8666 2487.4778 2488.6261 1.1264 6.1587 3.8757 555.8202 94.5444 39.1351 4.3655 6.1470 9.3086 6.1689 9.3099 11.9595 10.7021 29.0782 17.8041 89.5150 3.9255 0.6387 19.9467 0.5864 1196.2381 -299.8627 1147.2482 -500.3632 1047.2791 -500.3541 948.7566 -498.4394 849.4419 -499.2555 850.1122 -499.4071 0.0000 0.0000 0.0000 0.0000 34.9409 -500.1505 30.0319 -500.3283 26.0340 -500.1478 22.9523 -500.0377 23.0186 -500.4927 20.0202 -500.2203 17.9635 -499.9395 12.9903 -500.0810 19.9903 -499.1916

19439 rows × 87 columns

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 19439 entries, 0 to 19438
Data columns (total 87 columns):
 #   Column                                              Non-Null Count  Dtype         
---  ------                                              --------------  -----         
 0   date                                                19439 non-null  datetime64[ns]
 1   final.output.concentrate_ag                         19438 non-null  float64       
 2   final.output.concentrate_pb                         19438 non-null  float64       
 3   final.output.concentrate_sol                        19228 non-null  float64       
 4   final.output.concentrate_au                         19439 non-null  float64       
 5   final.output.recovery                               19439 non-null  float64       
 6   final.output.tail_ag                                19438 non-null  float64       
 7   final.output.tail_pb                                19338 non-null  float64       
 8   final.output.tail_sol                               19433 non-null  float64       
 9   final.output.tail_au                                19439 non-null  float64       
 10  primary_cleaner.input.sulfate                       19415 non-null  float64       
 11  primary_cleaner.input.depressant                    19402 non-null  float64       
 12  primary_cleaner.input.feed_size                     19439 non-null  float64       
 13  primary_cleaner.input.xanthate                      19335 non-null  float64       
 14  primary_cleaner.output.concentrate_ag               19439 non-null  float64       
 15  primary_cleaner.output.concentrate_pb               19323 non-null  float64       
 16  primary_cleaner.output.concentrate_sol              19069 non-null  float64       
 17  primary_cleaner.output.concentrate_au               19439 non-null  float64       
 18  primary_cleaner.output.tail_ag                      19435 non-null  float64       
 19  primary_cleaner.output.tail_pb                      19418 non-null  float64       
 20  primary_cleaner.output.tail_sol                     19377 non-null  float64       
 21  primary_cleaner.output.tail_au                      19439 non-null  float64       
 22  primary_cleaner.state.floatbank8_a_air              19435 non-null  float64       
 23  primary_cleaner.state.floatbank8_a_level            19438 non-null  float64       
 24  primary_cleaner.state.floatbank8_b_air              19435 non-null  float64       
 25  primary_cleaner.state.floatbank8_b_level            19438 non-null  float64       
 26  primary_cleaner.state.floatbank8_c_air              19437 non-null  float64       
 27  primary_cleaner.state.floatbank8_c_level            19438 non-null  float64       
 28  primary_cleaner.state.floatbank8_d_air              19436 non-null  float64       
 29  primary_cleaner.state.floatbank8_d_level            19438 non-null  float64       
 30  rougher.calculation.sulfate_to_au_concentrate       19437 non-null  float64       
 31  rougher.calculation.floatbank10_sulfate_to_au_feed  19437 non-null  float64       
 32  rougher.calculation.floatbank11_sulfate_to_au_feed  19437 non-null  float64       
 33  rougher.calculation.au_pb_ratio                     19439 non-null  float64       
 34  rougher.input.feed_ag                               19439 non-null  float64       
 35  rougher.input.feed_pb                               19339 non-null  float64       
 36  rougher.input.feed_rate                             19428 non-null  float64       
 37  rougher.input.feed_size                             19294 non-null  float64       
 38  rougher.input.feed_sol                              19340 non-null  float64       
 39  rougher.input.feed_au                               19439 non-null  float64       
 40  rougher.input.floatbank10_sulfate                   19405 non-null  float64       
 41  rougher.input.floatbank10_xanthate                  19431 non-null  float64       
 42  rougher.input.floatbank11_sulfate                   19395 non-null  float64       
 43  rougher.input.floatbank11_xanthate                  18986 non-null  float64       
 44  rougher.output.concentrate_ag                       19439 non-null  float64       
 45  rougher.output.concentrate_pb                       19439 non-null  float64       
 46  rougher.output.concentrate_sol                      19416 non-null  float64       
 47  rougher.output.concentrate_au                       19439 non-null  float64       
 48  rougher.output.recovery                             19439 non-null  float64       
 49  rougher.output.tail_ag                              19438 non-null  float64       
 50  rougher.output.tail_pb                              19439 non-null  float64       
 51  rougher.output.tail_sol                             19439 non-null  float64       
 52  rougher.output.tail_au                              19439 non-null  float64       
 53  rougher.state.floatbank10_a_air                     19438 non-null  float64       
 54  rougher.state.floatbank10_a_level                   19438 non-null  float64       
 55  rougher.state.floatbank10_b_air                     19438 non-null  float64       
 56  rougher.state.floatbank10_b_level                   19438 non-null  float64       
 57  rougher.state.floatbank10_c_air                     19438 non-null  float64       
 58  rougher.state.floatbank10_c_level                   19438 non-null  float64       
 59  rougher.state.floatbank10_d_air                     19439 non-null  float64       
 60  rougher.state.floatbank10_d_level                   19439 non-null  float64       
 61  rougher.state.floatbank10_e_air                     19003 non-null  float64       
 62  rougher.state.floatbank10_e_level                   19439 non-null  float64       
 63  rougher.state.floatbank10_f_air                     19439 non-null  float64       
 64  rougher.state.floatbank10_f_level                   19439 non-null  float64       
 65  secondary_cleaner.output.tail_ag                    19437 non-null  float64       
 66  secondary_cleaner.output.tail_pb                    19427 non-null  float64       
 67  secondary_cleaner.output.tail_sol                   17691 non-null  float64       
 68  secondary_cleaner.output.tail_au                    19439 non-null  float64       
 69  secondary_cleaner.state.floatbank2_a_air            19219 non-null  float64       
 70  secondary_cleaner.state.floatbank2_a_level          19438 non-null  float64       
 71  secondary_cleaner.state.floatbank2_b_air            19416 non-null  float64       
 72  secondary_cleaner.state.floatbank2_b_level          19438 non-null  float64       
 73  secondary_cleaner.state.floatbank3_a_air            19426 non-null  float64       
 74  secondary_cleaner.state.floatbank3_a_level          19438 non-null  float64       
 75  secondary_cleaner.state.floatbank3_b_air            19438 non-null  float64       
 76  secondary_cleaner.state.floatbank3_b_level          19438 non-null  float64       
 77  secondary_cleaner.state.floatbank4_a_air            19433 non-null  float64       
 78  secondary_cleaner.state.floatbank4_a_level          19438 non-null  float64       
 79  secondary_cleaner.state.floatbank4_b_air            19438 non-null  float64       
 80  secondary_cleaner.state.floatbank4_b_level          19438 non-null  float64       
 81  secondary_cleaner.state.floatbank5_a_air            19438 non-null  float64       
 82  secondary_cleaner.state.floatbank5_a_level          19438 non-null  float64       
 83  secondary_cleaner.state.floatbank5_b_air            19438 non-null  float64       
 84  secondary_cleaner.state.floatbank5_b_level          19438 non-null  float64       
 85  secondary_cleaner.state.floatbank6_a_air            19437 non-null  float64       
 86  secondary_cleaner.state.floatbank6_a_level          19438 non-null  float64       
dtypes: datetime64[ns](1), float64(86)
memory usage: 12.9 MB
None

В данных нет явных дубликатов
В данных есть пропущенные значения
In [7]:
# train data
data_train = read_file('train')
date final.output.concentrate_ag final.output.concentrate_pb final.output.concentrate_sol final.output.concentrate_au final.output.recovery final.output.tail_ag final.output.tail_pb final.output.tail_sol final.output.tail_au primary_cleaner.input.sulfate primary_cleaner.input.depressant primary_cleaner.input.feed_size primary_cleaner.input.xanthate primary_cleaner.output.concentrate_ag primary_cleaner.output.concentrate_pb primary_cleaner.output.concentrate_sol primary_cleaner.output.concentrate_au primary_cleaner.output.tail_ag primary_cleaner.output.tail_pb primary_cleaner.output.tail_sol primary_cleaner.output.tail_au primary_cleaner.state.floatbank8_a_air primary_cleaner.state.floatbank8_a_level primary_cleaner.state.floatbank8_b_air primary_cleaner.state.floatbank8_b_level primary_cleaner.state.floatbank8_c_air primary_cleaner.state.floatbank8_c_level primary_cleaner.state.floatbank8_d_air primary_cleaner.state.floatbank8_d_level rougher.calculation.sulfate_to_au_concentrate rougher.calculation.floatbank10_sulfate_to_au_feed rougher.calculation.floatbank11_sulfate_to_au_feed rougher.calculation.au_pb_ratio rougher.input.feed_ag rougher.input.feed_pb rougher.input.feed_rate rougher.input.feed_size rougher.input.feed_sol rougher.input.feed_au rougher.input.floatbank10_sulfate rougher.input.floatbank10_xanthate rougher.input.floatbank11_sulfate rougher.input.floatbank11_xanthate rougher.output.concentrate_ag rougher.output.concentrate_pb rougher.output.concentrate_sol rougher.output.concentrate_au rougher.output.recovery rougher.output.tail_ag rougher.output.tail_pb rougher.output.tail_sol rougher.output.tail_au rougher.state.floatbank10_a_air rougher.state.floatbank10_a_level rougher.state.floatbank10_b_air rougher.state.floatbank10_b_level rougher.state.floatbank10_c_air rougher.state.floatbank10_c_level rougher.state.floatbank10_d_air rougher.state.floatbank10_d_level rougher.state.floatbank10_e_air rougher.state.floatbank10_e_level rougher.state.floatbank10_f_air rougher.state.floatbank10_f_level secondary_cleaner.output.tail_ag secondary_cleaner.output.tail_pb secondary_cleaner.output.tail_sol secondary_cleaner.output.tail_au secondary_cleaner.state.floatbank2_a_air secondary_cleaner.state.floatbank2_a_level secondary_cleaner.state.floatbank2_b_air secondary_cleaner.state.floatbank2_b_level secondary_cleaner.state.floatbank3_a_air secondary_cleaner.state.floatbank3_a_level secondary_cleaner.state.floatbank3_b_air secondary_cleaner.state.floatbank3_b_level secondary_cleaner.state.floatbank4_a_air secondary_cleaner.state.floatbank4_a_level secondary_cleaner.state.floatbank4_b_air secondary_cleaner.state.floatbank4_b_level secondary_cleaner.state.floatbank5_a_air secondary_cleaner.state.floatbank5_a_level secondary_cleaner.state.floatbank5_b_air secondary_cleaner.state.floatbank5_b_level secondary_cleaner.state.floatbank6_a_air secondary_cleaner.state.floatbank6_a_level
0 2016-01-15 00:00:00 6.0554 9.8896 5.5073 42.1920 70.5412 10.4120 0.8954 16.9043 2.1431 127.0920 10.1283 7.25 0.9888 8.5476 10.3896 19.5293 34.1744 14.9365 2.5349 7.4761 2.1067 1549.7758 -498.9121 1551.4342 -516.4034 1549.8739 -498.6666 1554.3674 -493.4281 41885.7070 3481.7791 3520.3372 2.8387 6.1004 2.2849 523.5463 55.4866 36.8086 6.4861 11.9866 6.0080 11.8367 6.0058 11.5008 7.1011 28.0293 19.7938 87.1078 5.0080 0.5087 19.1543 1.1702 999.7069 -404.0670 1603.0114 -434.7150 1602.3750 -442.2045 1598.9373 -451.2941 1404.4720 -455.4630 1416.3550 -451.9396 14.5002 4.6948 8.7646 2.6062 25.8531 -498.5265 23.8937 -501.4063 23.9618 -495.2628 21.9404 -499.3410 14.0168 -502.4880 12.0999 -504.7159 9.9256 -498.3102 8.0797 -500.4710 14.1513 -605.8420
1 2016-01-15 01:00:00 6.0294 9.9689 5.2578 42.7016 69.2662 10.4627 0.9275 16.6345 2.2249 125.6292 10.2963 7.25 1.0027 8.5587 10.4971 19.3691 34.1185 16.2505 3.0496 6.7339 2.3530 1576.1667 -500.9050 1575.9506 -499.8659 1575.9942 -499.3151 1574.4793 -498.9317 42050.8618 3498.3710 3489.9819 2.8590 6.1611 2.2660 525.2906 57.2787 35.7534 6.4786 11.9712 6.0058 11.9962 6.0126 11.6159 7.2788 28.0671 20.0510 86.8433 4.9554 0.5367 18.9652 1.1848 1000.2864 -400.0652 1600.7546 -449.9534 1600.4796 -449.8306 1600.5276 -449.9536 1399.2271 -450.8698 1399.7195 -450.1190 14.2655 4.5925 9.0015 2.4882 25.8805 -499.9897 23.8895 -500.3724 23.9706 -500.0855 22.0857 -499.4469 13.9923 -505.5033 11.9505 -501.3315 10.0392 -500.1700 7.9848 -500.5822 13.9984 -599.7872
2 2016-01-15 02:00:00 6.0559 10.2140 5.3838 42.6575 68.1164 10.5070 0.9537 16.2088 2.2579 123.8198 11.3163 7.25 0.9913 8.6035 10.3545 19.1676 33.9695 16.4918 3.1247 6.4718 2.4168 1601.5562 -499.9978 1600.3867 -500.6078 1602.0035 -500.8701 1599.5415 -499.8274 42018.1012 3495.3489 3502.3598 2.9460 6.1165 2.1596 530.0266 57.5106 35.9716 6.3622 11.9206 6.1974 11.9203 6.2046 11.6958 7.2168 27.4540 19.7372 86.8423 4.8435 0.5464 18.8085 1.1626 999.7196 -400.0740 1599.3373 -450.0085 1599.6728 -449.9545 1599.8493 -449.9542 1399.1809 -449.9376 1400.3167 -450.5271 14.1157 4.6248 8.8429 2.4582 26.0052 -499.9296 23.8867 -499.9519 23.9135 -499.4423 23.9577 -499.9020 14.0150 -502.5209 11.9128 -501.1334 10.0709 -500.1291 8.0139 -500.5176 14.0287 -601.4274
3 2016-01-15 03:00:00 6.0480 9.9770 4.8586 42.6898 68.3475 10.4228 0.8838 16.5328 2.1468 122.2702 11.3221 7.25 0.9967 7.2219 8.4966 15.9785 28.2607 16.0244 2.9604 6.8438 2.2621 1599.9687 -500.9518 1600.6592 -499.6771 1600.3041 -500.7280 1600.4495 -500.0526 42029.4480 3498.5783 3499.1629 3.0023 6.0433 2.0378 542.5904 57.7927 36.8622 6.1182 11.6301 6.2032 11.6924 6.1966 11.9150 7.1756 27.3413 19.3208 87.2264 4.6553 0.5425 19.3302 1.0798 999.8148 -400.2002 1600.0594 -450.6199 1600.0128 -449.9105 1597.7252 -450.1301 1400.9432 -450.0301 1400.2347 -449.7908 13.7321 4.4825 9.1229 2.3221 25.9425 -499.1767 23.9555 -499.8488 23.9668 -500.0088 23.9544 -499.9447 14.0365 -500.8573 11.9996 -501.1937 9.9704 -499.2016 7.9773 -500.2559 14.0056 -599.9961
4 2016-01-15 04:00:00 6.1486 10.1425 4.9394 42.7741 66.9270 10.3603 0.7928 16.5257 2.0553 117.9882 11.9136 7.25 1.0099 9.0894 9.9868 19.1999 33.0449 16.4802 3.1121 6.5502 2.2771 1601.3397 -498.9755 1601.4379 -500.3232 1599.5819 -500.8882 1602.6495 -500.5930 42125.3542 3494.8008 3506.6793 3.1696 6.0609 1.7869 540.5319 56.0472 34.3477 5.6637 10.9578 6.1988 10.9605 6.1949 12.4111 7.2402 27.0410 19.2161 86.6888 4.5528 0.5154 19.2674 1.0126 999.6787 -399.7527 1600.2088 -449.5996 1600.3577 -450.0344 1599.7590 -449.9098 1401.5609 -448.8772 1401.1602 -450.4071 14.0800 4.4707 8.8710 2.3304 26.0248 -500.2791 23.9553 -500.5936 23.9857 -500.0838 23.9589 -499.9903 14.0273 -499.8386 11.9531 -501.0539 9.9257 -501.6867 7.8942 -500.3560 13.9966 -601.4967
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
14144 2018-08-18 06:59:59 3.2249 11.3562 6.8035 46.7140 73.7551 8.7696 3.1415 10.4032 1.5292 123.3818 8.0289 6.50 1.3042 6.5592 12.4460 7.0563 32.9402 22.8546 6.5315 3.9343 2.3144 1648.4212 -400.3822 1648.7420 -400.3597 1648.5782 -399.3636 1648.8340 -399.6692 45912.9426 2497.6530 2499.1780 1.1556 6.0919 4.6176 560.8891 85.7183 37.3698 5.3359 7.7628 9.1586 7.7667 9.1561 11.1249 10.9840 30.0689 18.6036 89.5744 4.2073 0.6129 16.6666 0.7486 1199.2459 -300.8455 1149.8079 -498.7897 1047.9636 -498.4131 946.6410 -499.1525 849.6649 -499.2145 849.7581 -497.4487 0.0000 0.0000 0.0000 0.0000 35.0432 -499.0457 29.9067 -499.9799 26.0024 -499.9534 22.9872 -499.9674 23.0315 -501.1679 20.0076 -499.7400 18.0060 -499.8344 13.0011 -500.1557 20.0078 -501.2964
14145 2018-08-18 07:59:59 3.1960 11.3494 6.8622 46.8668 69.0493 8.8973 3.1305 10.5495 1.6125 120.8782 7.9626 6.50 1.3024 6.6124 12.6239 7.5423 32.9253 23.0025 6.6297 4.0891 2.4629 1649.8202 -399.9310 1649.3575 -399.7212 1648.6562 -401.1958 1649.7251 -400.6363 46200.0996 2614.4039 2518.5501 1.1673 6.1213 4.1450 559.0318 119.4992 38.5916 4.8386 7.3567 9.3050 7.0955 9.2979 11.4260 10.8882 29.7840 18.4414 87.7240 4.1779 0.6506 16.9606 0.7716 1196.5693 -299.5122 1147.6752 -500.6083 1048.5657 -500.9328 949.7736 -500.0231 848.5152 -500.2894 850.0131 -496.8221 0.0000 0.0000 0.0000 0.0000 35.0261 -499.8919 29.9218 -499.9497 26.0317 -500.3846 22.9911 -500.0796 22.9601 -501.6128 20.0357 -500.2514 17.9985 -500.3952 12.9540 -499.8952 19.9685 -501.0416
14146 2018-08-18 08:59:59 3.1100 11.4344 6.8860 46.7957 67.0022 8.5296 2.9114 11.1151 1.5966 105.6661 7.9551 6.50 1.3159 7.1289 12.6336 7.9419 31.8567 22.2981 6.2783 4.4575 2.6122 1649.1668 -399.8886 1649.1969 -399.6776 1647.8970 -399.9883 1649.7727 -399.8319 44585.1813 2510.8135 2510.1424 1.1256 5.9705 4.0200 555.6829 122.2627 40.0740 4.5251 6.5860 9.2996 6.5841 9.3001 8.5235 8.9551 22.7865 15.1112 88.8906 4.0500 0.6361 18.3231 0.6851 1204.8666 -299.2357 1149.9429 -501.7179 1049.6044 -500.5491 952.7027 -502.3523 849.0160 -500.5057 850.4556 -506.8980 0.0000 0.0000 0.0000 0.0000 35.0036 -501.0838 29.9905 -611.8559 25.9484 -500.0673 22.9683 -499.8394 23.0157 -501.7116 19.9512 -499.8570 18.0195 -500.4512 13.0234 -499.9144 19.9909 -501.5185
14147 2018-08-18 09:59:59 3.3672 11.6256 6.7994 46.4082 65.5232 8.7772 2.8192 10.4638 1.6029 98.8805 7.9842 6.50 1.2420 7.4904 12.0124 6.9430 30.7709 22.7147 6.2531 3.5571 2.8006 1646.5478 -398.9771 1648.2122 -400.3833 1648.9174 -399.5213 1651.4986 -399.7453 44084.7357 2507.5725 2507.7234 1.1179 6.0481 3.9025 544.7317 123.7424 39.7139 4.3628 6.2101 9.2977 6.2095 9.2972 11.6588 10.6554 29.0527 17.8348 89.8581 3.9025 0.6256 19.4237 0.5671 1201.9042 -299.9073 1154.0878 -500.0366 1054.0098 -500.2373 944.1388 -496.8670 851.5898 -499.0405 851.3456 -499.1226 0.0000 0.0000 0.0000 0.0000 34.9807 -498.1310 29.9685 -586.0133 25.9717 -499.6084 22.9584 -499.8213 23.0250 -501.1534 20.0541 -500.3147 17.9795 -499.2729 12.9924 -499.9763 20.0140 -500.6255
14148 2018-08-18 10:59:59 3.5984 11.7378 6.7175 46.2994 70.2815 8.4067 2.5175 10.6522 1.3894 95.2484 8.0790 6.50 1.2830 7.5376 11.6503 6.2036 30.3566 22.7359 6.0843 3.0267 2.7080 1648.7599 -399.8621 1650.1354 -399.9573 1648.8319 -400.5861 1649.4646 -400.6733 44082.8666 2487.4778 2488.6261 1.1264 6.1587 3.8757 555.8202 94.5444 39.1351 4.3655 6.1470 9.3086 6.1689 9.3099 11.9595 10.7021 29.0782 17.8041 89.5150 3.9255 0.6387 19.9467 0.5864 1196.2381 -299.8627 1147.2482 -500.3632 1047.2791 -500.3541 948.7566 -498.4394 849.4419 -499.2555 850.1122 -499.4071 0.0000 0.0000 0.0000 0.0000 34.9409 -500.1505 30.0319 -500.3283 26.0340 -500.1478 22.9523 -500.0377 23.0186 -500.4927 20.0202 -500.2203 17.9635 -499.9395 12.9903 -500.0810 19.9903 -499.1916

14149 rows × 87 columns

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 14149 entries, 0 to 14148
Data columns (total 87 columns):
 #   Column                                              Non-Null Count  Dtype         
---  ------                                              --------------  -----         
 0   date                                                14149 non-null  datetime64[ns]
 1   final.output.concentrate_ag                         14148 non-null  float64       
 2   final.output.concentrate_pb                         14148 non-null  float64       
 3   final.output.concentrate_sol                        13938 non-null  float64       
 4   final.output.concentrate_au                         14149 non-null  float64       
 5   final.output.recovery                               14149 non-null  float64       
 6   final.output.tail_ag                                14149 non-null  float64       
 7   final.output.tail_pb                                14049 non-null  float64       
 8   final.output.tail_sol                               14144 non-null  float64       
 9   final.output.tail_au                                14149 non-null  float64       
 10  primary_cleaner.input.sulfate                       14129 non-null  float64       
 11  primary_cleaner.input.depressant                    14117 non-null  float64       
 12  primary_cleaner.input.feed_size                     14149 non-null  float64       
 13  primary_cleaner.input.xanthate                      14049 non-null  float64       
 14  primary_cleaner.output.concentrate_ag               14149 non-null  float64       
 15  primary_cleaner.output.concentrate_pb               14063 non-null  float64       
 16  primary_cleaner.output.concentrate_sol              13863 non-null  float64       
 17  primary_cleaner.output.concentrate_au               14149 non-null  float64       
 18  primary_cleaner.output.tail_ag                      14148 non-null  float64       
 19  primary_cleaner.output.tail_pb                      14134 non-null  float64       
 20  primary_cleaner.output.tail_sol                     14103 non-null  float64       
 21  primary_cleaner.output.tail_au                      14149 non-null  float64       
 22  primary_cleaner.state.floatbank8_a_air              14145 non-null  float64       
 23  primary_cleaner.state.floatbank8_a_level            14148 non-null  float64       
 24  primary_cleaner.state.floatbank8_b_air              14145 non-null  float64       
 25  primary_cleaner.state.floatbank8_b_level            14148 non-null  float64       
 26  primary_cleaner.state.floatbank8_c_air              14147 non-null  float64       
 27  primary_cleaner.state.floatbank8_c_level            14148 non-null  float64       
 28  primary_cleaner.state.floatbank8_d_air              14146 non-null  float64       
 29  primary_cleaner.state.floatbank8_d_level            14148 non-null  float64       
 30  rougher.calculation.sulfate_to_au_concentrate       14148 non-null  float64       
 31  rougher.calculation.floatbank10_sulfate_to_au_feed  14148 non-null  float64       
 32  rougher.calculation.floatbank11_sulfate_to_au_feed  14148 non-null  float64       
 33  rougher.calculation.au_pb_ratio                     14149 non-null  float64       
 34  rougher.input.feed_ag                               14149 non-null  float64       
 35  rougher.input.feed_pb                               14049 non-null  float64       
 36  rougher.input.feed_rate                             14141 non-null  float64       
 37  rougher.input.feed_size                             14005 non-null  float64       
 38  rougher.input.feed_sol                              14071 non-null  float64       
 39  rougher.input.feed_au                               14149 non-null  float64       
 40  rougher.input.floatbank10_sulfate                   14120 non-null  float64       
 41  rougher.input.floatbank10_xanthate                  14141 non-null  float64       
 42  rougher.input.floatbank11_sulfate                   14113 non-null  float64       
 43  rougher.input.floatbank11_xanthate                  13721 non-null  float64       
 44  rougher.output.concentrate_ag                       14149 non-null  float64       
 45  rougher.output.concentrate_pb                       14149 non-null  float64       
 46  rougher.output.concentrate_sol                      14127 non-null  float64       
 47  rougher.output.concentrate_au                       14149 non-null  float64       
 48  rougher.output.recovery                             14149 non-null  float64       
 49  rougher.output.tail_ag                              14148 non-null  float64       
 50  rougher.output.tail_pb                              14149 non-null  float64       
 51  rougher.output.tail_sol                             14149 non-null  float64       
 52  rougher.output.tail_au                              14149 non-null  float64       
 53  rougher.state.floatbank10_a_air                     14148 non-null  float64       
 54  rougher.state.floatbank10_a_level                   14148 non-null  float64       
 55  rougher.state.floatbank10_b_air                     14148 non-null  float64       
 56  rougher.state.floatbank10_b_level                   14148 non-null  float64       
 57  rougher.state.floatbank10_c_air                     14148 non-null  float64       
 58  rougher.state.floatbank10_c_level                   14148 non-null  float64       
 59  rougher.state.floatbank10_d_air                     14149 non-null  float64       
 60  rougher.state.floatbank10_d_level                   14149 non-null  float64       
 61  rougher.state.floatbank10_e_air                     13713 non-null  float64       
 62  rougher.state.floatbank10_e_level                   14149 non-null  float64       
 63  rougher.state.floatbank10_f_air                     14149 non-null  float64       
 64  rougher.state.floatbank10_f_level                   14149 non-null  float64       
 65  secondary_cleaner.output.tail_ag                    14147 non-null  float64       
 66  secondary_cleaner.output.tail_pb                    14139 non-null  float64       
 67  secondary_cleaner.output.tail_sol                   12544 non-null  float64       
 68  secondary_cleaner.output.tail_au                    14149 non-null  float64       
 69  secondary_cleaner.state.floatbank2_a_air            13932 non-null  float64       
 70  secondary_cleaner.state.floatbank2_a_level          14148 non-null  float64       
 71  secondary_cleaner.state.floatbank2_b_air            14128 non-null  float64       
 72  secondary_cleaner.state.floatbank2_b_level          14148 non-null  float64       
 73  secondary_cleaner.state.floatbank3_a_air            14145 non-null  float64       
 74  secondary_cleaner.state.floatbank3_a_level          14148 non-null  float64       
 75  secondary_cleaner.state.floatbank3_b_air            14148 non-null  float64       
 76  secondary_cleaner.state.floatbank3_b_level          14148 non-null  float64       
 77  secondary_cleaner.state.floatbank4_a_air            14143 non-null  float64       
 78  secondary_cleaner.state.floatbank4_a_level          14148 non-null  float64       
 79  secondary_cleaner.state.floatbank4_b_air            14148 non-null  float64       
 80  secondary_cleaner.state.floatbank4_b_level          14148 non-null  float64       
 81  secondary_cleaner.state.floatbank5_a_air            14148 non-null  float64       
 82  secondary_cleaner.state.floatbank5_a_level          14148 non-null  float64       
 83  secondary_cleaner.state.floatbank5_b_air            14148 non-null  float64       
 84  secondary_cleaner.state.floatbank5_b_level          14148 non-null  float64       
 85  secondary_cleaner.state.floatbank6_a_air            14147 non-null  float64       
 86  secondary_cleaner.state.floatbank6_a_level          14148 non-null  float64       
dtypes: datetime64[ns](1), float64(86)
memory usage: 9.4 MB
None

В данных нет явных дубликатов
В данных есть пропущенные значения
In [8]:
# test data
data_test = read_file('test')
date primary_cleaner.input.sulfate primary_cleaner.input.depressant primary_cleaner.input.feed_size primary_cleaner.input.xanthate primary_cleaner.state.floatbank8_a_air primary_cleaner.state.floatbank8_a_level primary_cleaner.state.floatbank8_b_air primary_cleaner.state.floatbank8_b_level primary_cleaner.state.floatbank8_c_air primary_cleaner.state.floatbank8_c_level primary_cleaner.state.floatbank8_d_air primary_cleaner.state.floatbank8_d_level rougher.input.feed_ag rougher.input.feed_pb rougher.input.feed_rate rougher.input.feed_size rougher.input.feed_sol rougher.input.feed_au rougher.input.floatbank10_sulfate rougher.input.floatbank10_xanthate rougher.input.floatbank11_sulfate rougher.input.floatbank11_xanthate rougher.state.floatbank10_a_air rougher.state.floatbank10_a_level rougher.state.floatbank10_b_air rougher.state.floatbank10_b_level rougher.state.floatbank10_c_air rougher.state.floatbank10_c_level rougher.state.floatbank10_d_air rougher.state.floatbank10_d_level rougher.state.floatbank10_e_air rougher.state.floatbank10_e_level rougher.state.floatbank10_f_air rougher.state.floatbank10_f_level secondary_cleaner.state.floatbank2_a_air secondary_cleaner.state.floatbank2_a_level secondary_cleaner.state.floatbank2_b_air secondary_cleaner.state.floatbank2_b_level secondary_cleaner.state.floatbank3_a_air secondary_cleaner.state.floatbank3_a_level secondary_cleaner.state.floatbank3_b_air secondary_cleaner.state.floatbank3_b_level secondary_cleaner.state.floatbank4_a_air secondary_cleaner.state.floatbank4_a_level secondary_cleaner.state.floatbank4_b_air secondary_cleaner.state.floatbank4_b_level secondary_cleaner.state.floatbank5_a_air secondary_cleaner.state.floatbank5_a_level secondary_cleaner.state.floatbank5_b_air secondary_cleaner.state.floatbank5_b_level secondary_cleaner.state.floatbank6_a_air secondary_cleaner.state.floatbank6_a_level
0 2016-09-01 00:59:59 210.8009 14.9931 8.0800 1.0050 1398.9813 -500.2256 1399.1449 -499.9197 1400.1030 -500.7044 1399.0017 -499.4851 13.1286 5.6367 489.7937 62.7105 42.0224 12.0841 16.9229 6.1535 16.8675 6.1507 1001.8493 -350.3013 1249.7386 -399.1075 1249.7505 -399.3967 1198.2879 -399.4886 999.4723 -399.5310 949.5662 -398.1808 24.9382 -500.4910 14.9476 -500.0138 20.0182 -450.3960 13.9871 -449.8315 12.0236 -497.7958 8.0167 -501.2891 7.9466 -432.3178 4.8725 -500.0374 26.7059 -499.7094
1 2016-09-01 01:59:59 215.3925 14.9875 8.0800 0.9905 1398.7779 -500.0574 1398.0554 -499.7782 1396.1510 -499.2402 1399.5081 -500.4163 13.0363 5.5261 490.1045 61.9612 41.1879 11.9190 17.0025 5.9990 16.9961 6.0023 998.6908 -350.4257 1248.3955 -399.9460 1249.5145 -399.6308 1200.5065 -399.9424 1000.0021 -399.4915 950.2000 -405.7875 24.9235 -499.8140 14.9309 -500.7645 19.9885 -450.1140 14.0930 -450.0592 12.0581 -498.6958 8.1310 -499.6342 7.9583 -525.8396 4.8789 -500.1624 25.0199 -499.8194
2 2016-09-01 02:59:59 215.2599 12.8849 7.7867 0.9960 1398.4937 -500.8684 1398.8604 -499.7645 1398.0757 -502.1515 1399.4971 -499.7155 13.1384 5.4266 489.6180 66.9038 42.5463 12.0913 16.9925 5.8506 16.9821 5.8536 998.5167 -349.7835 1247.4407 -400.2643 1248.2072 -401.0744 1199.7700 -400.7906 999.9255 -399.2370 950.3205 -400.8643 24.9087 -500.3040 14.9965 -500.9938 20.0397 -450.2632 14.0779 -449.6610 11.9624 -498.7675 8.0969 -500.8274 8.0711 -500.8017 4.9051 -499.8285 24.9949 -500.6226
3 2016-09-01 03:59:59 215.3362 12.0068 7.6400 0.8635 1399.6181 -498.8636 1397.4401 -499.2110 1400.1293 -498.3559 1401.0652 -501.0387 12.4005 5.1148 476.6181 59.8659 41.0602 12.1816 16.5321 5.8019 16.5153 5.8037 1000.2766 -350.1726 1251.3227 -398.6553 1250.4937 -399.7467 1199.3987 -397.5031 1001.9307 -400.4376 950.7353 -399.8030 24.8940 -499.3824 14.9167 -499.8623 20.0311 -449.3726 14.0140 -449.5269 12.0331 -498.3509 8.0749 -499.4744 7.8971 -500.8685 4.9314 -499.9636 24.9489 -498.7100
4 2016-09-01 04:59:59 199.0993 10.6825 7.5300 0.8056 1401.2681 -500.8083 1398.1288 -499.5045 1402.1722 -500.8106 1399.4805 -499.3741 11.3274 4.7674 488.2481 63.3153 41.2692 11.2904 13.6073 5.7381 13.6501 5.7396 996.5413 -350.5625 1304.6588 -399.5115 1306.4555 -399.0507 1248.6993 -400.8769 1058.8391 -398.9882 949.6453 -399.2776 24.8869 -499.3574 14.9792 -500.1868 19.9624 -450.6388 14.0092 -450.0223 12.0254 -500.7865 8.0547 -500.3975 8.1079 -509.5267 4.9577 -500.3600 25.0033 -500.8563
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
5285 2017-12-31 19:59:59 173.9578 15.9634 8.0700 0.8967 1401.9306 -499.7288 1401.4414 -499.1934 1399.8103 -499.5991 1400.3348 -499.2435 11.6390 4.6953 521.6852 66.7585 42.0220 10.5545 10.1092 7.2039 10.1108 7.1982 1399.9353 -299.9996 1049.6420 -500.0749 951.9239 -499.6488 852.3374 -498.4644 851.4368 -500.2856 851.4613 -499.7356 21.9121 -499.9152 19.9091 -499.7349 24.9396 -500.0985 12.9684 -499.9520 13.9960 -500.1575 12.0692 -499.6733 7.9773 -499.5161 5.9333 -499.9660 8.9872 -499.7559
5286 2017-12-31 20:59:59 172.9103 16.0026 8.0700 0.8965 1447.0757 -494.7168 1448.8519 -465.9630 1443.8904 -503.5877 1447.6534 -480.4038 11.3381 4.4784 530.1958 62.5607 41.0964 10.2541 9.9795 7.2009 9.9789 7.2020 1406.1223 -299.0572 1055.7152 -498.2502 956.8503 -497.4039 855.3867 -495.7027 852.0713 -499.4428 851.3290 -496.5798 25.3223 -497.0350 23.3106 -498.8926 26.3692 -499.2224 17.7049 -499.5457 16.7498 -496.0315 13.3654 -499.1227 9.2886 -496.8930 7.3729 -499.9430 8.9868 -499.9038
5287 2017-12-31 21:59:59 171.1357 15.9937 8.0700 1.1660 1498.8362 -501.7704 1499.5724 -495.5163 1502.7492 -520.6674 1502.5853 -498.2602 11.2859 4.4053 546.3069 73.6475 41.0955 10.1691 10.1822 7.1963 10.1855 7.1987 1396.2031 -300.2107 1047.8797 -499.6042 948.9634 -499.5496 856.3256 -500.0956 850.6146 -499.4737 850.2796 -498.5084 29.0759 -500.5284 26.8880 -500.9758 28.0002 -500.5659 22.9965 -500.4776 19.9941 -499.7913 15.1014 -499.9363 10.9892 -498.3479 9.0209 -500.0404 8.9820 -497.7899
5288 2017-12-31 22:59:59 179.6972 15.4390 8.0700 1.5011 1498.4662 -500.4840 1497.9870 -519.2003 1496.5690 -487.4796 1502.6889 -501.5697 11.2897 4.3435 547.3314 78.0664 41.7157 10.0712 10.7309 7.2001 10.7263 7.2008 1403.9423 -300.0301 1049.8023 -499.7956 950.7694 -500.3746 847.6472 -499.9203 849.4130 -500.3805 850.0750 -500.4782 29.1032 -499.6927 27.0517 -499.8583 27.9804 -499.9072 23.0466 -499.7449 19.9588 -499.9587 15.0269 -499.7231 11.0116 -499.9850 9.0098 -499.9379 9.0127 -500.1543
5289 2017-12-31 23:59:59 181.5569 14.9958 8.0700 1.6235 1498.0963 -499.7969 1501.7438 -505.1469 1499.5360 -492.4282 1499.6743 -499.7667 10.9046 4.0906 564.5404 77.6787 40.8911 9.6973 10.7895 7.1958 10.7929 7.2010 1400.6115 -299.9894 1052.7828 -499.7630 950.7937 -499.3572 850.6395 -500.4338 850.3708 -500.8194 850.6802 -498.5392 29.0981 -500.4299 26.8924 -500.0871 28.0235 -500.1704 23.0136 -500.2188 20.0347 -500.7286 14.9142 -499.9485 10.9866 -500.6580 8.9895 -500.3376 8.9886 -500.7649

5290 rows × 53 columns

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5290 entries, 0 to 5289
Data columns (total 53 columns):
 #   Column                                      Non-Null Count  Dtype         
---  ------                                      --------------  -----         
 0   date                                        5290 non-null   datetime64[ns]
 1   primary_cleaner.input.sulfate               5286 non-null   float64       
 2   primary_cleaner.input.depressant            5285 non-null   float64       
 3   primary_cleaner.input.feed_size             5290 non-null   float64       
 4   primary_cleaner.input.xanthate              5286 non-null   float64       
 5   primary_cleaner.state.floatbank8_a_air      5290 non-null   float64       
 6   primary_cleaner.state.floatbank8_a_level    5290 non-null   float64       
 7   primary_cleaner.state.floatbank8_b_air      5290 non-null   float64       
 8   primary_cleaner.state.floatbank8_b_level    5290 non-null   float64       
 9   primary_cleaner.state.floatbank8_c_air      5290 non-null   float64       
 10  primary_cleaner.state.floatbank8_c_level    5290 non-null   float64       
 11  primary_cleaner.state.floatbank8_d_air      5290 non-null   float64       
 12  primary_cleaner.state.floatbank8_d_level    5290 non-null   float64       
 13  rougher.input.feed_ag                       5290 non-null   float64       
 14  rougher.input.feed_pb                       5290 non-null   float64       
 15  rougher.input.feed_rate                     5287 non-null   float64       
 16  rougher.input.feed_size                     5289 non-null   float64       
 17  rougher.input.feed_sol                      5269 non-null   float64       
 18  rougher.input.feed_au                       5290 non-null   float64       
 19  rougher.input.floatbank10_sulfate           5285 non-null   float64       
 20  rougher.input.floatbank10_xanthate          5290 non-null   float64       
 21  rougher.input.floatbank11_sulfate           5282 non-null   float64       
 22  rougher.input.floatbank11_xanthate          5265 non-null   float64       
 23  rougher.state.floatbank10_a_air             5290 non-null   float64       
 24  rougher.state.floatbank10_a_level           5290 non-null   float64       
 25  rougher.state.floatbank10_b_air             5290 non-null   float64       
 26  rougher.state.floatbank10_b_level           5290 non-null   float64       
 27  rougher.state.floatbank10_c_air             5290 non-null   float64       
 28  rougher.state.floatbank10_c_level           5290 non-null   float64       
 29  rougher.state.floatbank10_d_air             5290 non-null   float64       
 30  rougher.state.floatbank10_d_level           5290 non-null   float64       
 31  rougher.state.floatbank10_e_air             5290 non-null   float64       
 32  rougher.state.floatbank10_e_level           5290 non-null   float64       
 33  rougher.state.floatbank10_f_air             5290 non-null   float64       
 34  rougher.state.floatbank10_f_level           5290 non-null   float64       
 35  secondary_cleaner.state.floatbank2_a_air    5287 non-null   float64       
 36  secondary_cleaner.state.floatbank2_a_level  5290 non-null   float64       
 37  secondary_cleaner.state.floatbank2_b_air    5288 non-null   float64       
 38  secondary_cleaner.state.floatbank2_b_level  5290 non-null   float64       
 39  secondary_cleaner.state.floatbank3_a_air    5281 non-null   float64       
 40  secondary_cleaner.state.floatbank3_a_level  5290 non-null   float64       
 41  secondary_cleaner.state.floatbank3_b_air    5290 non-null   float64       
 42  secondary_cleaner.state.floatbank3_b_level  5290 non-null   float64       
 43  secondary_cleaner.state.floatbank4_a_air    5290 non-null   float64       
 44  secondary_cleaner.state.floatbank4_a_level  5290 non-null   float64       
 45  secondary_cleaner.state.floatbank4_b_air    5290 non-null   float64       
 46  secondary_cleaner.state.floatbank4_b_level  5290 non-null   float64       
 47  secondary_cleaner.state.floatbank5_a_air    5290 non-null   float64       
 48  secondary_cleaner.state.floatbank5_a_level  5290 non-null   float64       
 49  secondary_cleaner.state.floatbank5_b_air    5290 non-null   float64       
 50  secondary_cleaner.state.floatbank5_b_level  5290 non-null   float64       
 51  secondary_cleaner.state.floatbank6_a_air    5290 non-null   float64       
 52  secondary_cleaner.state.floatbank6_a_level  5290 non-null   float64       
dtypes: datetime64[ns](1), float64(52)
memory usage: 2.1 MB
None

В данных нет явных дубликатов
В данных есть пропущенные значения

Посмотрим, какие данные были выделены для теста модели, а какие для обучения.

In [9]:
# percentage of data for test
f'Процент данных для теста: {len(data_test) / len(data_full):.0%}'
Out[9]:
'Процент данных для теста: 27%'
In [10]:
# train and test data
ax=data_train.plot(x='date', 
                y='rougher.input.feed_au',
                kind='scatter', c='gray', alpha=0.3, figsize=(15,5), label='Обучение')
data_test.plot(x='date', 
               y='rougher.input.feed_au',
               kind='scatter', c='c', alpha=0.3, label='Тест', ax=ax)

plt.xlabel('Дата')
plt.ylabel('% золота в руде')
plt.legend(title='Данные для модели', loc='upper left')
plt.title('Изменение доли золота в руде', fontsize=14)
plt.show()

Вывод: В данных более 19 000 записей (партий обработки руды) без явных дубликатов с параметрами исходной руды, флотации и очистки за три года, с 2016 по 2018. В некоторых признаках есть пропуски. Данные разделены на обучающую и тестовую выборки в соотношении 73% к 27%. Для обучающей выборки взяты данные каждого кода, примерно одних и тех же месяцев, а вот для тестовой выборки взяты два периода - с конец 2016 и 2017 годов (примерно с сентября по январь).

Сравним обучающую и тестовую выборки на примере распределения размеров гранул сырья. Если распределения будут сильно отличаться друг от друга, оценка модели может быть неправильной и стоит переразбить данные на выборки.

In [11]:
# distribution histograms of feed sizes and au concentrations
fig, axs = plt.subplots(1, 2, figsize=(10, 4))
fig.suptitle("Сравнение выборок", fontsize="x-large")

sns.histplot(data=data_full, x='rougher.input.feed_size', kde=True, stat= 'density', 
             color="salmon", ax=axs[0])
sns.histplot(data=data_train, x='rougher.input.feed_size', kde=True, stat= 'density',
             color="skyblue", ax=axs[0])
sns.histplot(data=data_test, x='rougher.input.feed_size', kde=True, stat= 'density',
             color="green", ax=axs[0]).set(xlabel='Размер гранул сырья', 
                                           ylabel=None, xlim=(0, 150))

sns.histplot(data=data_full, x='rougher.input.feed_au', kde=True, stat= 'density',
             color="salmon", ax=axs[1])
sns.histplot(data=data_train, x='rougher.input.feed_au', kde=True, stat= 'density',
             color="skyblue", ax=axs[1])
sns.histplot(data=data_test, x='rougher.input.feed_au', kde=True, stat= 'density',
             color="green", ax=axs[1]).set(xlabel='Доля золота в руде', 
                                           ylabel=None, xlim=(0, 20))

plt.legend(labels=["Полные данные",
                   "Обучающие данные",
                   "Тестовые данные"], 
           bbox_to_anchor=(1.01, 1), loc='upper left', borderaxespad=0)
plt.show()

Вывод: Судя по распределению размеров гранул сырья и количеству золота в руде, обучающая выборка очень похожа на весь набор данных. Тестовая выборка несколько отличается - для размера гранул она больше скошена вправо, а для распределения концентрации золота - влево. При этом в тестовой выборке преобладают значение с более высоким содержанием золота, по сравнению с обучающей выборкой. Тем не менее, диапазон значений схож и, так как мы имеем "фиксированное" разделение на тестовую и обучающую выборки, оставим их как есть.

Известно, что в тестовой выборке отсутствуют некоторые признаки, так как для прогнозирования целевых переменных нам доступны лишь "входные" данные, а некоторые параметры замеряются и/или рассчитываются значительно позже. В тестовых данных 53 признака, а в обучающих и исходных - 87, включая целевые. Посмотрим, каких признаков нет в тестовых данных.

In [12]:
# features unavalable in the test data
set(data_train) - set(data_test)
Out[12]:
{'final.output.concentrate_ag',
 'final.output.concentrate_au',
 'final.output.concentrate_pb',
 'final.output.concentrate_sol',
 'final.output.recovery',
 'final.output.tail_ag',
 'final.output.tail_au',
 'final.output.tail_pb',
 'final.output.tail_sol',
 'primary_cleaner.output.concentrate_ag',
 'primary_cleaner.output.concentrate_au',
 'primary_cleaner.output.concentrate_pb',
 'primary_cleaner.output.concentrate_sol',
 'primary_cleaner.output.tail_ag',
 'primary_cleaner.output.tail_au',
 'primary_cleaner.output.tail_pb',
 'primary_cleaner.output.tail_sol',
 'rougher.calculation.au_pb_ratio',
 'rougher.calculation.floatbank10_sulfate_to_au_feed',
 'rougher.calculation.floatbank11_sulfate_to_au_feed',
 'rougher.calculation.sulfate_to_au_concentrate',
 'rougher.output.concentrate_ag',
 'rougher.output.concentrate_au',
 'rougher.output.concentrate_pb',
 'rougher.output.concentrate_sol',
 'rougher.output.recovery',
 'rougher.output.tail_ag',
 'rougher.output.tail_au',
 'rougher.output.tail_pb',
 'rougher.output.tail_sol',
 'secondary_cleaner.output.tail_ag',
 'secondary_cleaner.output.tail_au',
 'secondary_cleaner.output.tail_pb',
 'secondary_cleaner.output.tail_sol'}

В тестовых данных, как и ожидалось, отсутствуют все "выходные" данные каждой технологической стадии процесса и рассчеты на их основе. Такие же переменные стоит оставить и в обучающей выборке.

Добавим в тестовые данные целевые переменные rougher.output.recovery и final.output.recovery и удалим из обучающего сета признаки, которых нет в тестовом.

In [13]:
# add targets to the test dataset
data_test = data_test.merge(
    data_full[
        ['date', 'rougher.output.recovery', 'final.output.recovery']], 
    on='date', how='left')
data_test.shape
Out[13]:
(5290, 55)
In [14]:
# drop "output" features from the train dataset
data_train = data_train[list(data_test)]
data_train.shape
Out[14]:
(14149, 55)

Теперь для подготовки модели у нас есть обучающая и тестовая выборки с одинаковыми столбцами. В данных имеются пропуски, которые перед обучением моделей стоит обработать. Обработку пропусков и возможных выбросов проведем после исседовательского анализа данных.

Проверка рассчетов¶

Перед обучением модели стоит проверить, правильно ли в данных рассчитаны целевые переменные rougher.output.recovery и final.output.recovery, так как от этого будет зависеть результат.

Расчёт эффективности

Эффективность обогащения рассчитывается по формуле

$$Recovery = C * (F - T) / (F * (C - T)) * 100\%$$

где:

  • C — доля золота в концентрате после флотации/очистки;
  • F — доля золота в сырье/концентрате до флотации/очистки;
  • T — доля золота в отвальных хвостах после флотации/очистки.

Создадим функцию для рассчета коэффициента восстановления.

In [15]:
# function for calculation of Recovery
def recovery(df, c, f, t):
    """Функция рассчитывает эффективность обогащения.
    Принимает на вход: 
    df - датафрейм; 
    с — строку с названием столбца с долей золота в концентрате 
    ПОСЛЕ флотации/очистки;
    f — строку с названием столбца с долей золота в сырье/концентрате 
    ДО флотации/очистки;
    t — строку с названием столбца с долей золота в отвальных хвостах 
    после флотации/очистки.
    Возвращает pd.Series c коэффициентами эффективности обогащения.
    """
    rec = (df[c] * (df[f]-df[t])
           .div(df[f]*(df[c]-df[t]))
           .mul(100))
    return rec
In [16]:
# calculation of recovery after rougher process
rec_rougher = recovery(data_full,
                       'rougher.output.concentrate_au',
                       'rougher.input.feed_au',
                       'rougher.output.tail_au')
In [17]:
# calculation of final Recovery
rec_final = recovery(data_full,
                     'final.output.concentrate_au',
                     'rougher.input.feed_au',
                     'final.output.tail_au')
In [18]:
print('Средний коэффициент эффективности обогащения:')
print()
print('Черновой концентрат:')
print(f'Рассчеты {rec_rougher.mean():.2f}')
print(f'Данные {data_full["rougher.output.recovery"].mean():.2f}')
print(f'MAE: {mean_absolute_error(data_full["rougher.output.recovery"], rec_rougher):.4f}')
print()
print('Финальный продукт:')
print(f'Рассчеты {rec_final.mean():.2f}')
print(f'Данные {data_full["final.output.recovery"].mean():.2f}')
print(f'MAE: {mean_absolute_error(data_full["final.output.recovery"], rec_final):.4f}')
Средний коэффициент эффективности обогащения:

Черновой концентрат:
Рассчеты 83.33
Данные 83.33
MAE: 0.0000

Финальный продукт:
Рассчеты 67.05
Данные 67.05
MAE: 0.0000

Вывод: целевые переменные в данных рассчитаны верно. Ошибка практически нулевая. Коэффициент эффективности обогащения для чернового концентрата составляет 83%, а для всего процесса - 67%.

Исследовательский анализ данных¶

Анализ признаков и их взаимодействий¶

Анализ данных проведем на целом датасете. Посмотрим, как различные признаки на разных стадиях влияют друг на друга и на целевые переменные.

Целевые признаки и доли золота в разных субстратах¶

Целевыми переменными являются рассчитанные коэффициенты эффективности обогащения для чернового концентрата rougher.output.recovery и для всего процесса final.output.recovery. Данные переменные зависят от доли золота в черновом концентрате/финальном концентрате, доли золота в руде и отвальных хвостах.

In [19]:
# boxplots
data_full[['rougher.output.recovery', 'final.output.recovery']].plot(
    kind='box', title='Коэффициенты обогащения', figsize=(10,5))
plt.ylabel('%')
plt.xticks([1,2], ['Черновой концентрат', 'Финальный продукт'])
plt.show()

#descriptive statistics
data_full[['rougher.output.recovery', 'final.output.recovery']].describe()
Out[19]:
rougher.output.recovery final.output.recovery
count 19439.0000 19439.0000
mean 83.3286 67.0502
std 14.1481 10.1258
min 0.0000 0.0000
25% 81.0380 63.2997
50% 86.1889 68.1727
75% 90.0107 72.6866
max 100.0000 100.0000

Коэффициент обогащения чернового концентрата в среднем составляет около 80%, для большинства случаев (трех четвертей) не превышает 90%. Для финального продукта эти цифры составляют 67% и 73% соответсвенно.

В целевых переменных rougher.output.recovery и final.output.recovery есть нулевые значения, что говорит о том, что были нулевые значения доли золота в черновом концентрате или финальном концентратах (в руде не было нулевых значений доли золота). Проанализируем эти значения, посмотрим для них на содержание золота и других металлов в руде и после флотации/очистки. Причина может быть в незаполнении этих показателей по каким-либо технологическим причинам, поскольку количество золота в руде ненулевое во всех случаях.

Также есть значения 100% восстановления золота, что странно, стоит их также проанализировать.

Нулевые значения коэффициентов обогащения

In [20]:
# boxplots
data_full[['rougher.output.concentrate_au', 
           'rougher.output.tail_au',
           'final.output.concentrate_au',
           'final.output.tail_au']].plot(
    kind='box', title='Распределения долей золота', figsize=(10,5))
plt.ylabel('%')
plt.xticks([1,2,3,4], ['Черновой концентрат', 
                       'Черновые отвалы', 
                       'Финальный продукт', 
                       'Финальные отвалы'])
plt.show()

data_full[['rougher.output.concentrate_au', 
           'rougher.output.tail_au',
           'final.output.concentrate_au',
           'final.output.tail_au']].describe()
Out[20]:
rougher.output.concentrate_au rougher.output.tail_au final.output.concentrate_au final.output.tail_au
count 19439.0000 19439.0000 19439.0000 19439.0000
mean 19.7720 1.8164 44.0765 3.0425
std 3.7536 0.6800 5.1298 0.9228
min 0.0000 0.0207 0.0000 0.0000
25% 18.8025 1.4042 43.4022 2.4611
50% 20.2802 1.8082 45.0112 2.9849
75% 21.7178 2.2119 46.2753 3.5714
max 28.8245 9.6890 52.7566 8.2450

Видно, что как в черновом, так и в финальном концентрате имеются нулевые значения доли золота, это, вероятно, является причиной нулевых значений коэффициентов восстановления. Сначала посмотрим на такие значения в черновом концентрате.

In [21]:
print('Процент данных с нулевым содержанием золота в черновом концентрате:')
print(f'{len(data_full[data_full["rougher.output.concentrate_au"] < 0.001])/len(data_full):.2%}')
Процент данных с нулевым содержанием золота в черновом концентрате:
2.03%

Посмотрим на другие признаки при "нулевых" концентрациях золота в черновом концентрате.

In [22]:
# [Au]=0 in rougher_output
data_full[data_full['rougher.output.concentrate_au'] < 0.001][['rougher.input.feed_au', 
                                                               'rougher.output.concentrate_au',
                                                               'rougher.output.tail_au',
                                                               'rougher.output.recovery']]
Out[22]:
rougher.input.feed_au rougher.output.concentrate_au rougher.output.tail_au rougher.output.recovery
45 7.1141 0.0 1.9398 0.0
46 7.6515 0.0 2.0876 0.0
47 5.5877 0.0 2.1265 0.0
48 4.7530 0.0 1.9389 0.0
49 5.1228 0.0 2.0509 0.0
... ... ... ... ...
15620 7.9511 0.0 0.7419 0.0
15621 8.7158 0.0 0.8280 0.0
15622 9.0261 0.0 1.0514 0.0
15623 8.7524 0.0 1.0643 0.0
15624 8.4676 0.0 1.0667 0.0

394 rows × 4 columns

In [23]:
# [Au]=0 in rougher_output, time distribution
ax = data_full.plot(x='date', 
                    y='rougher.input.feed_au',
                    kind='scatter', c='gray', alpha=0.2, figsize=(15,5), label='ненулевая')
data_full[data_full['rougher.output.concentrate_au'] < 0.001].plot(
    x='date', y='rougher.input.feed_au', kind='scatter', c='r', ax=ax,
    label='нулевая')

plt.xlabel('Дата')
plt.ylabel('% золота в руде')
plt.legend(title='Доля золота в черновом концентрате')
plt.title('Распределение "нулевых" долей золота в черновом концентрате')
plt.show()

Нулевые значения количества золота (2% данных) в черновом концентрате, вероятно, обусловлены какими-то технологическими ошибками, видно, что они появляются в основном "кластерами", и при этом количество золота в руде и отвальных хвостах ненулевое. Эти ошибки вызывают появление нулевых значений коэффициента обогащения, которые стоит обработать.

Можно видеть, что и у финального коэффициента обогащения есть нулевые значения, причиной этого также могут быть нулевые значения финальной концентрации золота. Проверим это.

In [24]:
print('Процент данных с нулевой концентрацией золота на финальной стадии:')
print(f'{len(data_full[data_full["final.output.concentrate_au"] < 0.001])/len(data_full):.2%}')
Процент данных с нулевой концентрацией золота на финальной стадии:
0.50%
In [25]:
# [Au]=0 in final.output
data_full[data_full['final.output.concentrate_au'] < 0.001][['rougher.input.feed_au', 
                                                             'final.output.concentrate_au',
                                                             'final.output.tail_au',
                                                             'rougher.output.recovery',
                                                             'final.output.recovery']]
Out[25]:
rougher.input.feed_au final.output.concentrate_au final.output.tail_au rougher.output.recovery final.output.recovery
644 5.5990 0.0 1.7791 68.9610 0.0
1193 7.7861 0.0 2.9845 82.7873 0.0
1194 7.4992 0.0 2.7473 83.5153 0.0
1195 7.0323 0.0 2.7914 83.7022 0.0
1196 7.0216 0.0 2.6821 84.8368 0.0
... ... ... ... ... ...
14951 11.6048 0.0 5.0618 79.7052 0.0
14952 11.7636 0.0 4.2916 83.3258 0.0
15183 7.3455 0.0 5.8195 26.9599 0.0
15184 7.6878 0.0 3.8373 78.7868 0.0
15310 6.0094 0.0 1.2109 65.0039 0.0

98 rows × 5 columns

In [26]:
# [Au]=0 in final.output, time distribution
ax = data_full.plot(x='date', 
                    y='rougher.input.feed_au',
                    kind='scatter', c='gray', alpha=0.2, 
                    figsize=(15,5), label='ненулевая')
data_full[data_full['final.output.concentrate_au'] < 0.001].plot(
    x='date', y='rougher.input.feed_au', kind='scatter', c='r', 
    ax=ax, label='нулевая')

plt.xlabel('Дата')
plt.ylabel('% золота в руде')
plt.legend(title='Доля золота в финальном продукте')
plt.title('Распределение "нулевых" долей золота в финальном продукте')
plt.show()

Нулевые значения количества золота в финальном продукте (0.5% данных) разбросаны по всем данным и, вероятно, также обусловлены какими-то технологическими ошибками. При этом количество золота в руде и отвальных хвостах ненулевое. Эти ошибки вызывают появление нулевых значений финального коэффициента обогащения, которые стоит обработать перед обучением модели.

100% коэффициенты обогащения

Имеются значения коэффициентов обогащения, равные 100%, что также подозрительно. Изучим их для чернового концентрата и финального сырья.

In [27]:
print('Процент данных с 100% обогащением, черновой концентрат:')
print(f'{len(data_full[data_full["rougher.output.recovery"] > 99.99])/len(data_full):.2%}')
Процент данных с 100% обогащением, черновой концентрат:
0.03%
In [28]:
# [Au]=100 in rougher_output
data_full[data_full['rougher.output.recovery'] > 99.99][['rougher.input.feed_au', 
                                                      'rougher.output.concentrate_au',
                                                      'rougher.output.tail_au',
                                                      'rougher.output.recovery']]
Out[28]:
rougher.input.feed_au rougher.output.concentrate_au rougher.output.tail_au rougher.output.recovery
16226 0.01 0.01 1.3525 100.0
16227 0.01 0.01 1.3525 100.0
16228 0.01 0.01 1.3525 100.0
16229 0.01 0.01 1.3525 100.0
16230 0.01 0.01 1.3525 100.0

В данных всего 5 значений, когда коэффициент обогащения черновоко концентрата равен 100%, в этом случае доля золота в руде равна доле в черновом концентрате. Это похоже на ошибочные значения.

In [29]:
print('Процент данных с 100% обогащением на финальной стадии:')
print(f'{len(data_full[data_full["final.output.recovery"] > 99.99])/len(data_full):.2%}')
Процент данных с 100% обогащением на финальной стадии:
0.47%
In [30]:
# [Au]=100 in final.output
data_full[data_full['final.output.recovery'] > 99.99][['rougher.output.concentrate_au',
                                                      'primary_cleaner.output.concentrate_au',
                                                      'final.output.concentrate_au',
                                                      'final.output.tail_au',
                                                      'final.output.recovery']]
Out[30]:
rougher.output.concentrate_au primary_cleaner.output.concentrate_au final.output.concentrate_au final.output.tail_au final.output.recovery
75 18.8615 5.9645 46.5433 0.0 100.0
1916 22.0130 10.3694 25.7735 0.0 100.0
1927 19.0389 34.1149 42.9840 0.0 100.0
2017 24.8120 28.5552 38.4562 0.0 100.0
3388 16.9222 25.1345 42.7182 0.0 100.0
... ... ... ... ... ...
18326 17.4802 35.1516 46.5568 0.0 100.0
18420 17.2792 28.7369 40.5272 0.0 100.0
19082 21.3183 35.4653 32.2661 0.0 100.0
19209 24.4854 30.8070 1.0640 0.0 100.0
19210 22.5045 32.5089 41.1188 0.0 100.0

91 rows × 5 columns

Видно, что 100% обогащение на финальной стадии является следствием нулевой концентрации золота в финальных отвальных хвостах. Посмотрим, как они распределены.

In [31]:
# [Au]=100 in final.output, time distribution
ax = data_full.plot(x='date', 
                    y='rougher.input.feed_au',
                    kind='scatter', c='gray', alpha=0.2, 
                    figsize=(15,5), label='ненулевые')
data_full[data_full['final.output.tail_au'] < 0.001].plot(
    x='date', y='rougher.input.feed_au', kind='scatter', c='r', 
    ax=ax, label='нулевые')

plt.xlabel('Дата')
plt.ylabel('% золота в руде')
plt.legend(title='Доля золота в отвальных хвостах')
plt.title('Распределение "нулевых" долей золота в финальных отвальных хвостах')
plt.show()

Нулевые значения количества золота в отвальных хвостах (0.47% данных) разбросаны по всем данным, а в паре случаев собраны в "кластеры". Это, вероятно, также следствие каких-то технологических ошибок. При этом концентрация золота после флотации, первой и второй очисток ненулевое. Эти ошибки вызывают появление нулевых значений финального коэффициента обогащения, которые стоит обработать перед обучением модели.

Вывод: кажется, что как для чернового концентрата, так и для финального сырья нулевые и 100% значения коэффициентов обогащения являются ошибочными и их стоит обработать перед обучением модели.

Размер гранул сырья и скорость их подачи¶

In [32]:
# scatterplots and distribution histograms, rougher
sns.pairplot(data_full[['rougher.input.feed_size',
                        'rougher.input.feed_rate',
                        'rougher.output.recovery']])                        
Out[32]:
<seaborn.axisgrid.PairGrid at 0x275b2e4abe0>
In [33]:
# scatterplots and distribution histograms, primary_cleaner
sns.pairplot(data_full[['primary_cleaner.input.feed_size',
                        'primary_cleaner.output.concentrate_au',
                        'primary_cleaner.output.tail_au',
                        'final.output.recovery']])                        
Out[33]:
<seaborn.axisgrid.PairGrid at 0x275b15edc70>
In [34]:
# descriptive statistics
data_full[['rougher.input.feed_size', 
           'rougher.input.feed_rate',
           'primary_cleaner.input.feed_size',
           'rougher.output.recovery',
           'final.output.recovery']].describe()
Out[34]:
rougher.input.feed_size rougher.input.feed_rate primary_cleaner.input.feed_size rougher.output.recovery final.output.recovery
count 19294.0000 19428.0000 19439.0000 19439.0000 19439.0000
mean 58.9703 478.3200 7.3071 83.3286 67.0502
std 21.6345 105.3738 0.6135 14.1481 10.1258
min 0.0464 0.0030 1.0800 0.0000 0.0000
25% 47.4351 416.5255 6.9400 81.0380 63.2997
50% 54.6102 499.4161 7.2800 86.1889 68.1727
75% 65.0230 550.1722 7.6700 90.0107 72.6866
max 484.9675 717.5088 15.5000 100.0000 100.0000

Вывод: Явно выраженного сильного влияния скорости подачи и размера гранул руды на коэффициент обогащения чернового концентрата не наблюдается, однако прослеживается некоторая тенденция - увеличение этого показателя при снижении скорости подачи и большем размере гранул.
Разброс размера гранул тут большой, в основном размер гранул около 60, и, как правило, не превышает 100, но есть выбросы и до почти 500, а минимальные значения около 0. Скорость подачи сырья в большинстве случаев от 200 до 600 (но есть пики около 0, 200, 400...).
Размер гранул чернового концетрата перед очисткой не оказывает особого влияния ни на концентрацию золота в продукте очистки и отвальных хвостах, ни на финальный коэффициент обогащения. Стоит отметить, что после флотации размер гранул более однородный (стандартное отклонение всего 0.6, в отличие от 22 перед флотацией), он более чем в 8 раз меньше и в среднем составляет около 7.

Флотационные реагенты¶

Сначала посмотрим на стадию флотации.

In [35]:
# scatterplots and distribution histograms, rougher
sns.pairplot(data_full[['rougher.input.floatbank10_sulfate',
                        'rougher.input.floatbank10_xanthate',
                        'rougher.input.floatbank11_sulfate',
                        'rougher.input.floatbank11_xanthate',
                        'rougher.output.recovery']]) 
Out[35]:
<seaborn.axisgrid.PairGrid at 0x275b40d0fd0>

Изучим подробнее влияние флотационных реагентов на эффективность обогащения чернового концентрата.

  • Влияние сульфидов на зависимость коэффициента обогащения чернового концентрата от доли золота в руде
In [36]:
# scatterplot
plt.scatter(data_full['rougher.input.feed_au'], 
            data_full['rougher.output.recovery'],
            c=data_full['rougher.input.floatbank10_sulfate'],
            alpha = .5, cmap = 'viridis', 
            vmin=5, vmax=20
           )
plt.colorbar(label='Количество сульфидов')
plt.title('Черновой концентрат')
plt.xlabel('Доля золота в руде, %')
plt.ylabel('Коэффициент обогащения, %')
plt.show()
  • Влияние ксантогенатов на зависимость коэффициента обогащения чернового концентрата от доли золота в руде
In [37]:
# scatterplot
plt.scatter(data_full['rougher.input.feed_au'], 
            data_full['rougher.output.recovery'],
            c=data_full['rougher.input.floatbank10_xanthate'],
            alpha = .5, cmap = 'viridis', 
            vmin=1, vmax=9
           )
plt.colorbar(label='Количество ксантогенатов')
plt.title('Черновой концентрат')
plt.xlabel('Доля золота в руде, %')
plt.ylabel('Коэффициент обогащения, %')
plt.show()

Вывод: количество флотационных реагентов некоторым образом влияет на коэффициент обогащения чернового концентрата, в большей степени - сульфида натрия. Флотационные установки достаточно "похожи" в этом ключе, поэтому одну из них можно исключить. Количество сульфида натрия и ксантогенатов связаны - чем больше одного реагента, тем больше и другого. Больше всего с высоким коэффициентом извлечения золота из бедных руд связано количество флотационного реагента сульфида натрия, для таких руд оно минимально.

Посмотрим, как связано количество флотационных реагентов при флотации и перед очисткой, и влияет ли содержание флотационных реагентов перед очисткой на финальный коэффициент обогащения.

In [38]:
# scatterplots and distribution histograms
sns.pairplot(data_full[['rougher.input.floatbank10_sulfate',
                        'rougher.input.floatbank10_xanthate',
                        'primary_cleaner.input.sulfate',
                        'primary_cleaner.input.depressant',
                        'primary_cleaner.input.xanthate',
                        'final.output.recovery']]) 
Out[38]:
<seaborn.axisgrid.PairGrid at 0x275b8ede970>
  • влияние сульфидов на зависимость финального коэффициента обогащения от доли золота в черновом концентрате
In [39]:
# scatterplot
plt.scatter(data_full['rougher.output.concentrate_au'], 
            data_full['final.output.recovery'],
            c=data_full['primary_cleaner.input.sulfate'],
            alpha = .5, cmap = 'viridis',
            vmin=25, vmax=250
           )
plt.colorbar(label='Количество сульфидов')
plt.title('Финальный продукт')
plt.xlabel('Доля золота в черновом концентрате, %')
plt.ylabel('Коэффициент обогащения, %')
plt.show()
  • влияние ксантогенатов на зависимость финального коэффициента обогащения от доли золота в черновом концентрате
In [40]:
# scatterplot
plt.scatter(data_full['rougher.output.concentrate_au'], 
            data_full['final.output.recovery'],
            c=data_full['primary_cleaner.input.xanthate'],
            alpha = .5, cmap = 'viridis',
            vmin=1, vmax=2
           )
plt.colorbar(label='Количество ксантогенатов')
plt.title('Финальный продукт')
plt.xlabel('Доля золота в черновом концентрате, %')
plt.ylabel('Коэффициент обогащения, %')
plt.show()

Вывод: Как правило, чем больше ксантогенатов в черновом концентрате перед очисткой, тем больше сульфида натрия.
Количество сульфида натрия и ксантогенатов в черновом концентрате перед очисткой некоторым образом влияет и на финальный коэффициент обогащения. Влияние ксантогенатов кажется более выраженным. Количество сульфидов во время флотации линейно связано с их количеством перед очисткой и некоторым образом с количеством ксантогенатов перед очисткой.
Не видно зависимости между финальным коэффициентом обогащения и долей золота в черновом концентрате, а также участия в этой взаимосвязи флотационных реагентов.

Физические параметры¶

Посмотрим на влияние объема воздуха и уровня жидкости во флотационных и очистительных установках на коэффициенты обогащения. Сначала посмотрим на стадию флотации и объем подаваемого воздуха.

In [41]:
# distribution histograms, rougher
data_full[['rougher.state.floatbank10_a_air',
           'rougher.state.floatbank10_b_air',
           'rougher.state.floatbank10_c_air',
           'rougher.state.floatbank10_d_air',
           'rougher.state.floatbank10_e_air',
           'rougher.state.floatbank10_f_air']].hist(bins=100, figsize=(10, 10))
plt.show()

В целом, разные флотационные установки схожи по объему подаваемого воздуха, за исключением 10_a. Для анализа можно выбрать какую-то одну, например, 10_b (репрезентативное симметричное распределение).

Посмотрим на уровень жидкости.

In [42]:
# distribution histograms, rougher
data_full[['rougher.state.floatbank10_a_level',
           'rougher.state.floatbank10_b_level',
           'rougher.state.floatbank10_c_level',
           'rougher.state.floatbank10_d_level',
           'rougher.state.floatbank10_e_level',
           'rougher.state.floatbank10_f_level']].hist(bins=100, figsize=(10, 10))
plt.show()

Флотационные установки схожи по уровню жидкости, опять же, за исключением 10_а. Также выберем одну - 10_b.

In [43]:
# scatterplots and distribution histograms, rougher
sns.pairplot(data_full[['rougher.state.floatbank10_b_air',
                        'rougher.state.floatbank10_b_level',
                        'rougher.output.recovery']]) 
Out[43]:
<seaborn.axisgrid.PairGrid at 0x275bd831be0>
  • Влияние объема воздуха на зависимость коэффициента обогащения чернового концентрата от доли золота в руде
In [44]:
# scatterplot
plt.scatter(data_full['rougher.input.feed_au'], 
            data_full['rougher.output.recovery'],
            c=data_full['rougher.state.floatbank10_b_air'],
            alpha = .5, cmap = 'viridis')

plt.colorbar(label='объема воздуха')
plt.title('Черновой концентрат')
plt.xlabel('Доля золота в руде, %')
plt.ylabel('Коэффициент обогащения, %')
plt.show()
  • Влияние уровня жидкости на зависимость коэффициента обогащения чернового концентрата от доли золота в руде
In [45]:
# scatterplot
plt.scatter(data_full['rougher.input.feed_au'], 
            data_full['rougher.output.recovery'],
            c=data_full['rougher.state.floatbank10_b_level'],
            alpha = .5, cmap = 'viridis')

plt.colorbar(label='Уровень жидкости')
plt.title('Черновой концентрат')
plt.xlabel('Доля золота в руде, %')
plt.ylabel('Коэффициент обогащения, %')
plt.show()

Вывод: Видно, что больше всего с высоким коэффициентом извлечения золота из бедных руд связано количество флотационного реагента сульфида натрия, для таких руд оно минимально. Таким образом, этот признак будет важным для модели. Для других "участников" процесса флотации такого влияния не видно.

Вывод: кажется, что объем воздуха не сильно влияет на коэффициент обогащения чернового концентрата, а вот с уровнем воды прослеживается некоторая тенденция связи. При более высоких уровнях минимальный коэффициент обогащения чернового концентрата выше.

Посмотрим, влияют ли физические факторы на финальный коэффициент обогащения. Поскольку мы видели, что флотационные установки схожи, выберем лишь какую-то одну для каждого этапа очистки.

In [46]:
# scatterplots and distribution histograms, primary_cleaner
sns.pairplot(data_full[['primary_cleaner.state.floatbank8_b_air',
                        'primary_cleaner.state.floatbank8_b_level',
                        'final.output.recovery']], diag_kind='kde')
Out[46]:
<seaborn.axisgrid.PairGrid at 0x275bef607c0>
In [47]:
# scatterplots and distribution histograms, secondary_cleaner
sns.pairplot(data_full[['secondary_cleaner.state.floatbank3_b_air',
                        'secondary_cleaner.state.floatbank3_b_level',
                        'final.output.recovery']], diag_kind='kde')
Out[47]:
<seaborn.axisgrid.PairGrid at 0x275bf514a00>

Вывод: кажется, что объем воздуха и уровень жидкости в установках на стадиях очистки не сильно влияет на финальный коэффициент обогащения.

Доля металлов в руде¶

Из данных видно, что попутными компонентами этой золотоносной руды являются серебро (ag), свинец (pb) и, вероятно, зольные компоненты (sol). Эффективность работы обогатительных фабрик во многом зависит от качества перерабатываемого сырья. Посмотрим, как связана доля металлов руде с коэффициентами обогащения золота.

In [48]:
# scatterplots and distribution histograms, rougher
sns.pairplot(data_full[['rougher.input.feed_au',
                        'rougher.input.feed_ag',
                        'rougher.input.feed_pb',
                        'rougher.input.feed_sol',
                        'rougher.output.recovery',
                        'final.output.recovery']])  
Out[48]:
<seaborn.axisgrid.PairGrid at 0x275bfb94880>
In [49]:
# descriptive statistics
data_full[['rougher.input.feed_au',
           'rougher.input.feed_ag',
           'rougher.input.feed_pb',
           'rougher.input.feed_sol']].describe()
Out[49]:
rougher.input.feed_au rougher.input.feed_ag rougher.input.feed_pb rougher.input.feed_sol
count 19439.0000 19439.0000 19339.0000 19340.0000
mean 8.2661 8.7949 3.5981 36.6951
std 1.9551 1.9368 1.0495 5.1454
min 0.0100 0.0100 0.0100 0.0100
25% 6.8544 7.2380 2.8873 34.2078
50% 8.1258 8.5936 3.5349 37.2026
75% 9.7699 10.2138 4.2442 40.0391
max 13.7313 14.5960 7.1426 53.4777

Мы видели, что есть руды, с более бедным содержанием золота, но достаточно высоким коэффициентом обогащения, проверим предположение, что в отвальных хвостах таких руд содержится меньше золота, а значит оно лучше извлекается.

In [50]:
# scatterplot
plt.scatter(data_full['rougher.input.feed_au'], 
            data_full['rougher.output.concentrate_au'],
            c=data_full['rougher.output.tail_au'],
            alpha = .5, cmap = 'viridis',
            vmin=0, vmax=8
           )
plt.colorbar(label='Отвальные хвосты')
plt.title('Доля золота, %')
plt.xlabel('Руда')
plt.ylabel('Черновой концентрат')
plt.show()

Действительно, видно, что для некоторых руд с меньшей долей золота, его доля в "отвальных хвостах" также минимальна (тут, конечно, играет роль и тот факт, если параметры обогащения схожи, что чем меньше золота в руде, тем меньше его будет и в отвальных хвостах).

Посмотрим на коэффициенты корреляции между долями компонентов в руде и коэффициентами обогащения.

In [51]:
# Phik correlation coefficients matrix
phik_overview = data_full[['rougher.input.feed_au',
                       'rougher.input.feed_ag',
                       'rougher.input.feed_pb',
                       'rougher.input.feed_sol',
                       'rougher.output.recovery',
                       'final.output.recovery']].phik_matrix()
interval columns not set, guessing: ['rougher.input.feed_au', 'rougher.input.feed_ag', 'rougher.input.feed_pb', 'rougher.input.feed_sol', 'rougher.output.recovery', 'final.output.recovery']
In [52]:
# Plot Phik correlation coefficients matrix
plot_correlation_matrix(phik_overview.values, 
                        x_labels=('feed_au', 'feed_ag', 'feed_pb', 'feed_sol',
                                  'rougher_recovery', 'final_recovery'),
                        y_labels=('feed_au', 'feed_ag', 'feed_pb', 'feed_sol',
                                  'rougher_recovery', 'final_recovery'), 
                        title="Phik correlation coefficients", 
                        figsize=(10, 5),
                        vmin=0, vmax=1)

Как было видно из гистограмм распределения, есть сильная корреляция между количеством золота в руде и количеством серебра, чуть меньшая - с количеством свинца. Коэффициент обогащения чернового концентрата имеет умеренную корреляцию с количеством каждого металла и зольного компонента в руде (около 0.5), а финальный коэффициент обогащения более слабо коррелирует с количеством компонентов в руде (наиболее выражено - для золота и серебра).

Вывод: Видна некоторая закономерная связь между количеством металлов в руде и коэффициентом обогащения чернового концентрата - чем больше доля металлов в сырье, тем больше коэффициент обогащения. Но связь не четко линейная, видно, что иногда из более бедных руд можно извлечь почти все золото. Вероятно, из некоторого сырья золото извлекается проще, в таком случае меньше золота будет в отвальных хвостах. Вероятно, важную роль тут могут играть другие факторы технологического процесса - количество флотационных реагентов, возможно, физические параметры. Видна такая зависимость - если в руде есть определенная доля цветного металла, то можно рассчитывать на определенный минимальный коэффициент обогащения, в зависимости от руды и условий он может быть значительно выше, но, как праввило, не будет ниже.
На финальный коэффициент обогащения содержание металлов в руде влияет не так сильно, что может быть следствием как раз разной эффективности извлечения металлов из руды. Прослеживается некоторая зависимость для золота (что логично) и серебра, для свинца - почти не видно влияния. Зольные компоненты на коэффициенты обогащения не влияют.

Видно четкую связь доли золота в руде с долями серебра и свинца - чем выше доля золота, тем выше и доля этих металлов. Доля золота и серебра в руде, поступающей на данное предприятие, в среднем около 8% (максимально - около 14%), свинца меньше - около 3.5% (максимально около 7%). Количество зольных компонентов может доходить до 50%, но иногда минимально, около 0.

Изменение концентрации металлов на различных этапах¶

Посмотрим, как меняется концентрация металлов (Au, Ag, Pb) и зольных компонентов после флотации и двух стадий очитски.

In [53]:
# distribution histograms
fig, axs = plt.subplots(2, 2, figsize=(7, 7))
fig.suptitle("Распределение долей компонентов на каждой стадии", fontsize="x-large")

sns.histplot(data=data_full, x='rougher.input.feed_au', kde=True, color="skyblue", ax=axs[0, 0])
sns.histplot(data=data_full, x='rougher.output.concentrate_au', kde=True, color="green", ax=axs[0, 0])
sns.histplot(data=data_full, x='primary_cleaner.output.concentrate_au', kde=True, color="salmon", ax=axs[0, 0])
sns.histplot(data=data_full, x='final.output.concentrate_au', kde=True, color="gold", ax=axs[0, 0]).set(
    title='Золото', xlabel='%', ylabel='Количество партий')

sns.histplot(data=data_full, x='rougher.input.feed_ag', kde=True, color="skyblue", ax=axs[0, 1])
sns.histplot(data=data_full, x='rougher.output.concentrate_ag', kde=True, color="green", ax=axs[0, 1])
sns.histplot(data=data_full, x='primary_cleaner.output.concentrate_ag', kde=True, color="salmon", ax=axs[0, 1])
sns.histplot(data=data_full, x='final.output.concentrate_ag', kde=True, color="gold", ax=axs[0, 1]).set(
    title='Серебро', xlabel='%', ylabel='Количество партий')

sns.histplot(data=data_full, x='rougher.input.feed_pb', kde=True, color="skyblue", ax=axs[1, 0])
sns.histplot(data=data_full, x='rougher.output.concentrate_pb', kde=True, color="green", ax=axs[1, 0])
sns.histplot(data=data_full, x='primary_cleaner.output.concentrate_pb', kde=True, color="salmon", ax=axs[1, 0])
sns.histplot(data=data_full, x='final.output.concentrate_pb', kde=True, color="gold", ax=axs[1, 0]).set(
    title='Свинец', xlabel='%', ylabel='Количество партий')

sns.histplot(data=data_full, x='rougher.input.feed_sol', kde=True, color="skyblue", ax=axs[1, 1])
sns.histplot(data=data_full, x='rougher.output.concentrate_sol', kde=True, color="green", ax=axs[1, 1])
sns.histplot(data=data_full, x='primary_cleaner.output.concentrate_sol', kde=True, color="salmon", ax=axs[1, 1])
sns.histplot(data=data_full, x='final.output.concentrate_sol', kde=True, color="gold", ax=axs[1, 1]).set(
    title='Зольные компоненты', xlabel='%', ylabel='Количество партий')

plt.tight_layout()
plt.legend(labels=["Руда",
                   "Черновой концентрат",
                   "Первичная очистка",
                   "Финальный концентрат"],
          bbox_to_anchor=(1.01, 1), loc='upper left', borderaxespad=0)
plt.show()

Посмотрим на изменение суммарной концентрации всех веществ на разных стадиях: в сырье, в черновом и финальном концентратах.

In [54]:
# distribution histograms
sns.histplot(data=data_full, 
             x=data_full[['rougher.input.feed_au',
                          'rougher.input.feed_ag',
                          'rougher.input.feed_pb', 
                          'rougher.input.feed_sol']].sum(axis=1), 
             kde=True, color="skyblue")

sns.histplot(data=data_full, 
             x=data_full[['rougher.output.concentrate_au',
                          'rougher.output.concentrate_ag',
                          'rougher.output.concentrate_pb',
                          'rougher.output.concentrate_sol']].sum(axis=1), 
             kde=True, color="green")

sns.histplot(data=data_full, 
             x=data_full[['primary_cleaner.output.concentrate_au',
                          'primary_cleaner.output.concentrate_ag',
                          'primary_cleaner.output.concentrate_pb',
                          'primary_cleaner.output.concentrate_sol']].sum(axis=1),
             kde=True, color="salmon")
sns.histplot(data=data_full, 
             x=data_full[['final.output.concentrate_au',
                          'final.output.concentrate_ag',
                          'final.output.concentrate_pb',
                          'final.output.concentrate_sol']].sum(axis=1),
             kde=True, color="gold").set(title='Распределение сумм долей компонентов', 
                                         xlabel='%', 
                                         ylabel='Количество партий')

plt.legend(labels=["Руда",
                   "Черновой концентрат",
                   "Первичная очистка",
                   "Финальный концентрат"])
plt.show()

Вывод: Видно, что в процессе обогащения концентрация золота увеличивается: в среднем около 10% в руде, около 20% в черновом концентрате, около 30% после первой очистки и более 40% в финальном продукте. Для свинца наблюдается похожая динамика, за исключением того, что вторая очистка уже не приводит к увеличению концентрации. Для серебра картина несколько иная - флотация увеличивает его концентрацию в черновом концентрате, однако последующие очистки снижают его количество в концентратах. Зольные компоненты уменьшаются в доле на каждом этапе технологического процесса, что может говорить о том, что как раз от них и стараются избавиться.

Обработка аномальных значений¶

Как видно из гистограмм распределения концентраций металлов и как мы видели в ранее в анализе - в целевых переменных - коэффициентах обогащения - есть аномальные значения, равные 0 и 100. Нулевые значения связанны с нулевой концентрацией золота в черновом и в финальном концентратах соответсвенно. 100% извлечение золота на финальном этапе связано с нулевыми значениями его концентрации в отвальных хвостах. Причина аномалий - скорее всего какие-то технологические ошибки.

Посмотрим, какой процент составляют аномальные значения в тестовой и обучающей выборках.

In [55]:
anom_train=len(data_train[(data_train["rougher.output.recovery"] < 0.001) | 
               (data_train["rougher.output.recovery"] > 99.99) |
               (data_train["final.output.recovery"] < 0.001) |
               (data_train["final.output.recovery"] > 99.99)])/len(data_train)

anom_test=len(data_test[(data_test["rougher.output.recovery"] < 0.001) | 
             (data_test["rougher.output.recovery"] > 99.99) |
             (data_test["final.output.recovery"] < 0.001) |
             (data_test["final.output.recovery"] > 99.99)])/len(data_test)

print('Процент аномальных значений коэффициентов обогащения:')
print(f'Обучающая выборка {anom_train:.2%}')
print(f'Тестовая выборка {anom_test:.2%}')
Процент аномальных значений коэффициентов обогащения:
Обучающая выборка 3.02%
Тестовая выборка 2.87%

Аномальные значения составляют около 3% как в обучающей, так и в тестовой выборках. Поскольку мы не можем заменить значения в целевых переменных - удалим их из обучающей выборки.

In [56]:
# drop 0 and 100 from the training data
data_train = data_train[(
    (data_train["rougher.output.recovery"] > 0.001) & (data_train["rougher.output.recovery"] < 99.99)) & (
    (data_train["final.output.recovery"] > 0.001) & (data_train["final.output.recovery"] < 99.99))]

print('Нулевые или =100% коэффициенты обогащения в обучающей выборке:')
print(len(data_train[(data_train["rougher.output.recovery"] < 0.001) | 
               (data_train["rougher.output.recovery"] > 99.99) |
               (data_train["final.output.recovery"] < 0.001) |
               (data_train["final.output.recovery"] > 99.99)]))
Нулевые или =100% коэффициенты обогащения в обучающей выборке:
0

Обработка пропущенных значений¶

В данных имелись пропущенные значения, для построения модели их необходимо обработать.

In [57]:
# number of NaN
print(f'Количество пропущенных значений в train (N={len(data_train)})')
print(data_train.isna().sum()[data_train.isna().sum() > 0]
      .sort_values(ascending=False), '\n')

print(f'Количество пропущенных значений в test (N={len(data_test)})')
print(data_test.isna().sum()[data_test.isna().sum() > 0]
      .sort_values(ascending=False))
Количество пропущенных значений в train (N=13721)
rougher.input.floatbank11_xanthate            374
rougher.state.floatbank10_e_air               368
secondary_cleaner.state.floatbank2_a_air      211
rougher.input.feed_size                       139
rougher.input.feed_pb                         100
primary_cleaner.input.xanthate                 87
rougher.input.feed_sol                         58
rougher.input.floatbank11_sulfate              29
rougher.input.floatbank10_sulfate              19
primary_cleaner.input.depressant               15
rougher.input.feed_rate                         8
secondary_cleaner.state.floatbank2_b_air        7
primary_cleaner.input.sulfate                   7
secondary_cleaner.state.floatbank3_a_air        4
secondary_cleaner.state.floatbank4_a_air        4
rougher.input.floatbank10_xanthate              3
primary_cleaner.state.floatbank8_d_air          3
primary_cleaner.state.floatbank8_a_air          2
primary_cleaner.state.floatbank8_c_air          2
primary_cleaner.state.floatbank8_b_air          2
secondary_cleaner.state.floatbank3_b_air        1
secondary_cleaner.state.floatbank3_b_level      1
secondary_cleaner.state.floatbank6_a_air        1
secondary_cleaner.state.floatbank5_a_level      1
secondary_cleaner.state.floatbank4_a_level      1
secondary_cleaner.state.floatbank4_b_air        1
secondary_cleaner.state.floatbank4_b_level      1
secondary_cleaner.state.floatbank5_a_air        1
secondary_cleaner.state.floatbank5_b_air        1
secondary_cleaner.state.floatbank3_a_level      1
secondary_cleaner.state.floatbank5_b_level      1
rougher.state.floatbank10_b_level               1
secondary_cleaner.state.floatbank2_b_level      1
secondary_cleaner.state.floatbank2_a_level      1
rougher.state.floatbank10_c_level               1
rougher.state.floatbank10_c_air                 1
rougher.state.floatbank10_b_air                 1
rougher.state.floatbank10_a_level               1
rougher.state.floatbank10_a_air                 1
primary_cleaner.state.floatbank8_d_level        1
primary_cleaner.state.floatbank8_c_level        1
primary_cleaner.state.floatbank8_b_level        1
primary_cleaner.state.floatbank8_a_level        1
secondary_cleaner.state.floatbank6_a_level      1
dtype: int64 

Количество пропущенных значений в test (N=5290)
rougher.input.floatbank11_xanthate          25
rougher.input.feed_sol                      21
secondary_cleaner.state.floatbank3_a_air     9
rougher.input.floatbank11_sulfate            8
primary_cleaner.input.depressant             5
rougher.input.floatbank10_sulfate            5
primary_cleaner.input.sulfate                4
primary_cleaner.input.xanthate               4
rougher.input.feed_rate                      3
secondary_cleaner.state.floatbank2_a_air     3
secondary_cleaner.state.floatbank2_b_air     2
rougher.input.feed_size                      1
dtype: int64

Посмотрим, как были распределены пропуски некоторых признаков во времени на полных данных.

In [58]:
# distribution of NaNs in time
ax = data_full.plot(x='date', 
                    y='rougher.input.feed_au',
                    kind='scatter', c='gray', alpha=0.2, 
                    figsize=(15,5), label='not NAN')
data_full[data_full['rougher.input.feed_size'].isna()].plot(
    x='date', y='rougher.input.feed_au', kind='scatter', c='r', 
    ax=ax, label='NAN, rougher.input.feed_size')
data_full[data_full['rougher.input.feed_pb'].isna()].plot(
    x='date', y='rougher.input.feed_au', kind='scatter', c='c', 
    ax=ax, label='NAN, rougher.input.feed_pb')
data_full[data_full['rougher.input.feed_sol'].isna()].plot(
    x='date', y='rougher.input.feed_au', kind='scatter', c='b', 
    ax=ax, label='NAN, rougher.input.feed_sol')

plt.xlabel('Дата')
plt.ylabel('Доля золота в руде, %')
plt.legend(title='Пропущенные значения')
plt.title('Распределение пропущенных значений в некоторых признаках')
plt.show()

Видно, что для некоторых переменных значения пропущены "кластерами" - вероятно в следствие ошибок в измерениях. Отсутсвие же измерений размера поступающих во флотационную установку гранул сырья распределено равномерно по данным, что может говорить о том, что иногда его, вероятно, затруднительно определить (маленькие гранулы?).

Поскольку отсутвующие данные распределены по датасету или сгруппированы в одних диапазонах значений - заполним их соседними значениями, тем более, что в техническом задании было указано, что соседние показатели обычно схожи.

In [59]:
# fill NAN in all dataset
data_full = data_full.fillna(method='ffill')
print('Количество пропусков в полном датасете:',
      data_full.isna().sum()[data_full.isna().sum() > 0])
Количество пропусков в полном датасете: Series([], dtype: int64)

Заполним пропуски в тестовых и обучающих данных значениями из полного датасета.

In [60]:
# fill NAN in training and test data
data_train = (data_train
              .set_index('date')
              .fillna(data_full.set_index('date')))

data_test = (data_test
             .set_index('date')
             .fillna(data_full.set_index('date')))
In [61]:
print('Количество пропусков в обучающих данных:', 
      data_train.isna().sum()[data_train.isna().sum() > 0])
print('Количество пропусков в тестовых данных:', 
      data_test.isna().sum()[data_test.isna().sum() > 0])
Количество пропусков в обучающих данных: Series([], dtype: int64)
Количество пропусков в тестовых данных: Series([], dtype: int64)

Исследование моделей¶

Для прогнозирования коэффициентов обогащения проверим две модели - линейную регрессию и случайный лес. Будем проверять модели отдельно для прогнозирования коэффициентов обогощения чернового и финального концентратов.

В качестве метрики качества моделей будем использовать симметричное среднее абсолютное процентное отклонение, sMAPE, одинаково учитывающее масштаб и целевого признака, и предсказания.

Напишем функции для вычисления sMAPE и итогового sMAPE, обобщающего качество модели для предсказания коэффициентов обогащения чернового и финального концентратов.

In [62]:
# define function to calculate Symmetric Mean Absolute Percentage Error (sMAPE)
def smape_score(y_true, y_pred):
    y_true = np.array(y_true)
    res = np.mean(
        np.abs(y_true - y_pred) / ((np.abs(y_true) + np.abs(y_pred))*0.5)
    )*100
    return res
In [63]:
# define function to calculate final sMAPE
def final_smape(smape_rougher, smape_final):
    return (smape_rougher*0.25 + smape_final*0.75)
    

Прогноз коэффициента обогащения чернового концентрата¶

Подготовка данных для моделей

Основываясь на проведенном анализе признаков, для построения модели исключим:

  • rougher.input.feed_ag, так как он практически "повторяет" rougher.input.feed_au
  • rougher.input.floatbank10_xanthate, так как он коррелирует с rougher.input.floatbank10_sulfate, а последний как кажется, имеет большую связь с целевым признаком.
  • rougher.input.floatbank11, так как они практически "повторяют" rougher.input.floatbank10
  • rougher.state.floatbank10_a, c, d, e, f, так как они практически "повторяют" rougher.state.floatbank10_b
In [64]:
# training data for "Rougher recovery" model
data_train_rougher = data_train[['rougher.input.feed_pb',
                                 'rougher.input.feed_rate',
                                 'rougher.input.feed_size',
                                 'rougher.input.feed_sol',
                                 'rougher.input.feed_au',
                                 'rougher.input.floatbank10_sulfate',
                                 'rougher.state.floatbank10_b_air',
                                 'rougher.state.floatbank10_b_level',
                                 'rougher.output.recovery']]
data_train_rougher.shape
Out[64]:
(13721, 9)
In [65]:
# test data for "Rougher recovery" model
data_test_rougher = data_test[list(data_train_rougher)]
data_test_rougher.shape
Out[65]:
(5290, 9)
In [66]:
# targets and features
x_train_rougher = data_train_rougher.drop('rougher.output.recovery', axis=1)
y_train_rougher = data_train_rougher['rougher.output.recovery']

x_test_rougher = data_test_rougher.drop('rougher.output.recovery', axis=1)
y_test_rougher = data_test_rougher['rougher.output.recovery']

Линейная регрессия¶

In [67]:
# Create the model
model_lr = LinearRegression()

# Cross-validation
scores_lr = cross_val_score(model_lr, x_train_rougher, y_train_rougher, cv=5, 
                            scoring=make_scorer(smape_score))
In [68]:
score_lr = scores_lr.mean()
print(f'Среднee sMAPE: {score_lr:.2f} со стандартным отклонением {scores_lr.std():.2f}')
Среднee sMAPE: 6.73 со стандартным отклонением 1.73

Случайный лес¶

In [69]:
# Create the model
model_rf = RandomForestRegressor(random_state=123)

# Hyperparameters to check
params = {'max_depth': range(1, 30), 'n_estimators': range(10, 200, 10)}
In [70]:
# Cross-validation
grid_model_rougher = RandomizedSearchCV(model_rf, params, 
                                        n_jobs=-1, 
                                        random_state=123, cv=5,
                                        scoring=make_scorer(smape_score, greater_is_better=False))

# Fit the model on the training data
grid_model_rougher.fit(x_train_rougher, y_train_rougher)

print(f'Гиперпараметры лучшей модели: {grid_model_rougher.best_params_}')
print(f'sMAPE: {abs(grid_model_rougher.best_score_):.2f}')
Гиперпараметры лучшей модели: {'n_estimators': 190, 'max_depth': 8}
sMAPE: 6.54

Cлучайная модель¶

In [71]:
# Create the dummy model
dummy_regr = DummyRegressor(strategy="mean")

# Cross-validation
scores_dr = cross_val_score(dummy_regr, x_train_rougher, y_train_rougher, cv=5, 
                            scoring=make_scorer(smape_score, greater_is_better=False))

score_dr = scores_dr.mean()

print(f'sMAPE случайной модели: {abs(score_dr):.2f} со стандартным отклонением {scores_dr.std():.2f}')
sMAPE случайной модели: 7.80 со стандартным отклонением 1.70

Вывод: Случайный лес для регрессии из 190 деревьев глубиной 8 показал самую маленькую ошибку, она почти на 1.5% меньше, чем у случайной модели.

Прогноз коэффициента обогащения финального продукта¶

Подготовка данных для моделей

Основываясь на проведенном анализе признаков, для построения модели возьмем признаки из первой модели, кроме самого "неважного" - rougher.input.feed_rate, а также не будем брать физические параметры флотации - уровень жидкости и объем воздуха, их возьмем из второго этапа очистки.

Кроме этого, из этапов очистки добавим primary_cleaner.input.xanthate, primary_cleaner.input.sulfate, физические параметры secondary_cleaner.state.floatbank3_b_air, level.

Не будем также включать: primary_cleaner.input.feed_size, так как размеры достаточно однородные и не видно их влияния, primary_cleaner.input.depressant, так как его влияния не видно.

In [72]:
# training data for "Final recovery" model
data_train_final = data_train[['primary_cleaner.input.sulfate',
                               'primary_cleaner.input.xanthate',
                               'rougher.input.feed_pb',
                               'rougher.input.feed_size',
                               'rougher.input.feed_sol',
                               'rougher.input.feed_au',
                               'rougher.input.floatbank10_sulfate',
                               'secondary_cleaner.state.floatbank3_b_air',
                               'secondary_cleaner.state.floatbank3_b_level',
                               'final.output.recovery']]
data_train_final.shape
Out[72]:
(13721, 10)
In [73]:
# test data for "Final recovery" model
data_test_final = data_test[list(data_train_final)]
data_test_final.shape
Out[73]:
(5290, 10)
In [74]:
# targets and features
x_train_final = data_train_final.drop('final.output.recovery', axis=1)
y_train_final = data_train_final['final.output.recovery']

x_test_final = data_test_final.drop('final.output.recovery', axis=1)
y_test_final = data_test_final['final.output.recovery']

Линейная регрессия¶

In [75]:
# Create the model
model_lr = LinearRegression()

# Cross-validation
scores_lr = cross_val_score(model_lr, x_train_final, y_train_final, cv=5,
                            scoring=make_scorer(smape_score, greater_is_better=False))
In [76]:
score_lr = scores_lr.mean()
print(f'Средняя оценка качества модели: {abs(score_lr):.2f} со стандартным отклонением {scores_lr.std():.2f}')
Средняя оценка качества модели: 9.46 со стандартным отклонением 1.51

Случайный лес¶

In [77]:
# Create the model
model_rf = RandomForestRegressor(random_state=123)

# Hyperparameters to check
params = {'max_depth': range(1, 30), 'n_estimators': range(10, 200, 10)}
In [78]:
# Cross-validation
grid_model = RandomizedSearchCV(model_rf, params, n_jobs=-1, random_state=123, cv=5,
                                scoring=make_scorer(smape_score, greater_is_better=False))

# Fit the model on the training data
grid_model.fit(x_train_final, y_train_final)

print(f'Гиперпараметры лучшей модели: {grid_model.best_params_}')
print(f'sMAPE: {abs(grid_model.best_score_):.2f}')
Гиперпараметры лучшей модели: {'n_estimators': 70, 'max_depth': 5}
sMAPE: 9.95

Cлучайная модель¶

In [79]:
# Create the dummy model
dummy_regr = DummyRegressor(strategy="mean")

# Cross-validation
scores_dr = cross_val_score(dummy_regr, x_train_final, y_train_final, cv=5, 
                            scoring=make_scorer(smape_score, greater_is_better=False))

score_dr = scores_dr.mean()

print(f'sMAPE случайной модели: {abs(score_dr):.2f} со стандартным отклонением {scores_dr.std():.2f}')
sMAPE случайной модели: 10.30 со стандартным отклонением 0.94

Вывод: Самую маленькую ошибку показала модель Линейной регрессии, sMAPE=9.46, что почти на 1% меньше, чем у случайной модели.

Тестирование моделей¶

Предсказание коэффициента обогащения для чернового концентрата¶

In [80]:
# Create the model
model_rougher = RandomForestRegressor(max_depth=8, n_estimators=190, random_state=12345)

# Fit the model on the training data
model_rougher.fit(x_train_rougher, y_train_rougher)
Out[80]:
RandomForestRegressor(max_depth=8, n_estimators=190, random_state=12345)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
RandomForestRegressor(max_depth=8, n_estimators=190, random_state=12345)
In [81]:
# Make predictions on the test data
pred_test_rougher = model_rougher.predict(x_test_rougher)
In [82]:
# Create the dummy model
dummy_regr = DummyRegressor(strategy="mean")
# Fit the model on the training data
dummy_regr.fit(x_train_rougher, y_train_rougher)
# Make predictions on the test data
pred_dummy_rougher = dummy_regr.predict(x_test_rougher)
In [83]:
# Evaluate the model
smape_model_rougher = smape_score(y_test_rougher, pred_test_rougher)
smape_dummy_rougher = smape_score(y_test_rougher, pred_dummy_rougher)

print('sMAPE:', round(smape_model_rougher, 4))
print('sMAPE случайной модели:', round(smape_dummy_rougher, 4))
sMAPE: 8.1025
sMAPE случайной модели: 9.0755

Посмотрим на то, какие признаки были важными для принятия решения моделью:

In [84]:
# Importance of the features for RandomForest
feature_importances = pd.DataFrame(
    {'features': list(x_train_rougher.columns),
     'feature_importance': model_rougher.feature_importances_})

# plot feature importances
feature_importances.sort_values(by='feature_importance', ascending=False).plot(kind='bar', x='features')
plt.ylabel('Feature_importance')
plt.xlabel('')
Out[84]:
Text(0.5, 0, '')

Вывод: Качество модели, проверенное на тестовой выборке получилось выше, чем у случайной модели. На основании данной модели можно предсказывать значения коэффициентов обогащения чернового концентрата, ошибаясь лишь в 8% случаев. Самым важным признаком для предсказания является содержание золота в руде, что логично. Важен также размер гранул сырья, а вот скорость их подачи большой роли не играет.

Предсказание коэффициента обогащения для финального продукта¶

In [85]:
# Create the model
model_final = LinearRegression()

# Fit the model on the training data
model_final.fit(x_train_final, y_train_final)
Out[85]:
LinearRegression()
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
LinearRegression()
In [86]:
# Make predictions on the test data
pred_test_final = model_final.predict(x_test_final)
In [87]:
# Create the dummy model
dummy_regr = DummyRegressor(strategy="mean")
# Fit the model on the training data
dummy_regr.fit(x_train_final, y_train_final)
# Make predictions on the test data
pred_dummy_final = dummy_regr.predict(x_test_final)
In [88]:
# Evaluate the model
smape_model_final = smape_score(y_test_final, pred_test_final)
smape_dummy_final = smape_score(y_test_final, pred_dummy_final)

print('sMAPE:', round(smape_model_final, 4))
print('sMAPE случайной модели:', round(smape_dummy_final, 4))
sMAPE: 9.4904
sMAPE случайной модели: 10.1791

Вывод: Качество модели, проверенное на тестовой выборке получилось почти на 1% выше, чем у случайной модели. На основании данной модели можно предсказывать финальные значения коэффициентов обогащения, ошибаясь лишь в 9,5% случаев.

Итоговая метрика¶

Рассчитаем итоговую оценку качества моделей.

In [89]:
f'Итоговая sMAPE: {final_smape(smape_model_rougher, smape_model_final):.2f}'
Out[89]:
'Итоговая sMAPE: 9.14'

Общий вывод:¶

Из данных видно, что руды, используемые на данном предприятии, являются комплексными - в них содержится поровну золота и серебра, а также свинец, причем их соотношение как правило линейно зависимо - чем больше золота в руде, тем больше и серебра и свинца. Содержание золота не очень высокое - 8%, но из более бедных руд иногда удается извлечь больше золота. Это указывает не только на то, что руды отличаются между собой, но и на то, что стоит обращать внимание на процесс обогащения и его параметры, возможно, они также могут значимо влиять на коэффициенты обогащения. Например, более бедные руды также может быть выгодно использовать - некоторые из них могут иметь высокий коэффициент обогащения с использованием низкого количества флотационного реагента сульфида натрия, что может снижать затраты обогащения.

С каждой стадией технологического процесса доля золота "на выходе" повышается примерно на 10%, для других металлов такой четкой зависимости нет, что говорит о том, что процесс оптимизирован под обогащение золота. Возможно, стоит дополнительно отдельно проанализировать его содержание в отвальных хвостах и связь этого параметра с другими.

Коэффициент обогащения чернового концентрата как правило составляет 80-90%, а финального концентрата чуть ниже - 60-70%. Из анализа данных видно, что больше всего на коэффициент обогащения чернового концентрата влияет доля золота и других металлов в руде, некоторое влияние имеют и размер гранул сырья, количество флотационных реагентов. На финальный коэффициент обогащения также влияет концентрация металлов в руде, но в меньшей степени, плюс, содержание флотационных реагентов в черновом концентрате перед очисткой.

В данных есть пропущенные и аномальные значения. Так, концентрация золота на "выходе" технологического этапа или в отвальных хвостах могла равняться 0. Проанализировав такие случаи кажется, что это связано с какими-то технологическими ошибками, а не особенностями сырья. Для обучения модели пропущенные значения были заменены на ближайшие соседние, а аномальные в обучающей выборке - удалены.

Анализ данных позволил разработать модели для предсказания значений коэффициентов обогащения в черновом и финальном концентратах. Итоговая оценка качества моделей составляет около 9%, то есть используя такой прогноз мы будем ошибаться примерно в 9% случаев. Разработка модели машинного обучения для предсказания коэффициента восстановления золота из золотосодержащей руды поможет оценивать перспективность руды и оптимизировать параметры ее обогащения для более эффективного производства.

In [ ]: